Cascading Style Sheet。

概述

简介

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 优点:

  • 使数据和显示分开。
  • 降低网络流量。
  • 使整个网站视觉效果一致。
  • 使开发效率提高了。耦合性降低,一个人负责写 html,一个人负责写 css。

样式表嵌入网页的方法

  • 行内样式:在某个特定的标签里采用 style 属性。范围只针对此标签。

    <p style="color: white; background-color: red;"></p>
    
  • 内嵌样式表:在页面的 head 里采用 <style> 标签。范围针对此页面。需要确保元素的样式定义在开始和结束样式标签之间。 需要确保所有样式规则位于花括号之间,并且每条样式规则都以分号结束。

    <style type="text/css"></style>
    
  • 引入外部样式表

    • 采用 <link> 标签。link是XTHML标签,无兼容问题。

      <link rel="stylesheet" type="text/css" href="cssfile.css"></link>
      

      rel 属性还可以是 alternate stylesheet,用户可以选择。

      你可以通过给 <link> 加上 media 属性来指定该样式文件只能对什么设备生效,不指定的话默认是 all,即对所有设备都生效:

      <link rel="stylesheet" src="styles.css" media="screen" />
      
    • 采用 import:必须写在 <style> 标签中,并且必须是第一句。

      <style>
       @import url(cssfile.css);
      </style>
      
    • 区别:外部样式表中不能写 <link> 标签,但是可以写 import 语句。link引用css时,页面加载同时加载样式,@import需要页面完全载入以后加载;

在 CSS 文件中,除了注释、CSS 规则集以及 @规则 外,定义的一些别的东西都将被浏览器忽略。

使用 CSS 时可能会遇到浏览器兼容性问题。当浏览器解析页面的 CSS 时,会自动忽视不能识别或者不支持的属性。 此时,浏览器会尝试使用其它值。 但如果没有找到其它值,则会使用默认值。这意味着如果想提供浏览器降级方案,在声明之前提供另一个更宽泛的值即可。 这样老旧的浏览器会降级使用这个方案,新的浏览器会在后面的声明里覆盖降级方案。

CSS 语法

  • CSS 的核心功能是将 CSS 属性设定为特定的值。一个属性与值的键值对被称为声明(declaration)。CSS 允许重复声明某个样式,这时最后声明的键值对会覆盖前面的键值对。
  • 而如果将一个或者多个声明用 {} 包裹起来后,那就组成了一个声明块(declaration block)。多个声明的顺序并不重要。声明块可以写成多行,也可以写成一行。缩进和换行只是为了增加可读性,CSS 引擎会忽略它们。
  • 声明块如果需要作用到对应的 HTML 元素,那还需要加上选择器。选择器和声明块组成了CSS 规则集(CSS ruleset),常简称为 CSS 规则。
  • 规则集中最后一条声明可以省略分号,但是并不建议这么做,因为容易出错。
  • 如果一个属性有多个值的话,那么多个值用空格隔开。

CSS 注释

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

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

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

注意:注释不能嵌套。

CSS 属性值定义语法

符号 名称 描述 示例
组合符号
并置 各部分必须出现且按顺序出现 solid <length>
&& “与”组合符 各部分必须出现,但可以不按顺序 <length> && <string>
` ` “或”组合符
` ` “互斥”组合符 各部分恰好出现一个
[ ] 方括号 强调优先级 bold [ thin && <length> ]
数量符号
无数量符号 恰好一次 solid
* 星号 零次、一次或多次 bold smaller*
+ 加号 一次或多次 bold smaller+
? 问号 零次或一次(即可选) bold smaller?
{A,B} 大括号 至少A次,至多B bold smaller{1,3}
# 井号 一次或多次,但多次出现必须以逗号分隔 bold smaller#
! 叹号 组必须产生一个值 [ bold? smaller? ]!

@规则

CSS 规则是样式表的主体,通常样式表会包括大量的规则列表。但有时候也需要在样式表中包括其他的一些信息,比如字符集,导入其它的外部样式表,字体等,这些需要专门的语句表示。

而 @规则 就是这样的语句。CSS 里包含了以下 @规则:

  • @namespace 告诉 CSS 引擎必须考虑XML命名空间。
  • @page, 描述打印文档时布局的变化.
  • @font-face, 描述将下载的外部的字体。
  • @keyframes, 描述 CSS 动画的关键帧。
  • @document, 如果文档样式表满足给定条件则条件规则组里的规则生效。

@media

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

@media CSS @规则 可用于基于一个或多个媒体查询的结果来应用样式表的一部分。 使用它,您可以指定一个媒体查询和一个CSS块,当且仅当该媒体查询与正在使用其内容的设备匹配时,该CSS块才能应用于该文档。

@media media-type and (media-feature-rule) {
  /* CSS rules go here */
}

@media 规则可置于您代码的顶层或位于其它任何@条件规则组内。

媒体查询由媒体类型组成,如果媒体类型与展示网页的设备类型匹配,则应用对应的样式。媒体类型之前,还可以使用notonly关键字。

@media not screen {
   
}

@media only screen {
   
}

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

下面是一个媒体查询的例子,当设备宽度小于或等于 100px 时返回内容:

@media (max-width: 100px) { /* CSS Rules */ }

以下定义的媒体查询,是当设备高度大于或等于 350px 时返回内容:

@media (min-height: 350px) { /* CSS Rules */ }

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

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

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

media type

  • all:适用于所有设备;
  • print:适用于在打印预览模式下在屏幕上查看的分页材料和文档;
  • screen:主要用于屏幕;
  • speech:主要用于语音合成器。

需要注意的是:通过 media 指定的资源尽管不匹配它的设备类型,但是浏览器依然会加载它。如果要覆盖某个样式,最好放在要覆盖样式之后,可以认为 @media 是一个 if 语句。

逻辑操作符

  • and:查询条件都满足的时候才生效;
  • not:查询条件取反;
  • only:整个查询匹配的时候才生效,常用语兼容旧浏览器,使用时候必须指定媒体类型;
  • 逗号或者 or:查询条件满足一项即可匹配;
/* 用户设备的最小高度为680px或为纵向模式的屏幕设备 */
@media (min-height: 680px), screen and (orientation: portrait) {}

max-height,min-height,height一起使用时,优先级问题

实际效果:

  • 当 height 和 max-height一起使用时,谁小听谁的
    • max-height < height 元素高度: max-height
    • height < max-height 元素高度: height
  • 当 height,max-height,min-height一起使用时
    • height > max-height > min-height 元素高度:max-height
    • height > min-height > max-height 元素高度:min-height
    • min-height > height > max-height 元素高度:min-height

Level 5 新的媒体查询特性

可以查询到用户在设备上的喜好设置。比如:

  • prefers-reduced-motion
  • prefers-contrast
  • prefers-reduced-transparency
  • prefers-color-scheme
  • inverted-colors

使用的方式和以往我们熟悉的 @media 是相似。比如 prefers-color-scheme 实现暗黑查式的皮肤切换效果:

// 代码源于 https://codepen.io/airen/full/ProgLL
// dark & light mode
:root {
  /* Light theme */
  --c-text: #333;
  --c-background: #fff;
}

body {
  color: var(--c-text);
  background-color: var(--c-background);
}

@media (prefers-color-scheme: dark) {
  :root {
    /* Dark theme */
    --c-text: #fff;
    --c-background: #333;
  } 
}

@charset

@charset 用于定义样式表使用的字符集。它必须是样式表中的第一个元素。如果有多个 @charset 被声明,只有第一个会被使用,而且不能在HTML元素或HTML页面的 <style> 元素内使用。

@charset "UTF-8";

某个样式表文件到底用的是什么字符编码,浏览器有一套识别顺序(优先级由高到低):

  • 文件开头的 Byte order mark 字符值,不过一般编辑器并不能看到文件头里的 BOM 值;

  • HTTP 响应头里的 content-type 字段包含的 charset 所指定的值,比如:

    Content-Type: text/css; charset=utf-8
    
  • CSS 文件头里定义的 @charset 规则里指定的字符编码;

  • <link> 标签里的 charset 属性,该条已在 HTML5 中废除;

  • 默认是 UTF-8。

@import

@import CSS@规则,用于从其他样式表导入样式规则。这些规则必须先于所有其他类型的规则,@charset 规则除外; 因为它不是一个嵌套语句,@import不能在条件组的规则中使用。

因此,用户代理可以避免为不支持的媒体类型检索资源,作者可以指定依赖媒体的@import规则。这些条件导入在URI之后指定逗号分隔的媒体查询。在没有任何媒体查询的情况下,导入是无条件的。指定所有的媒体具有相同的效果。

@import url;
@import url list-of-media-queries;

url 是一个表示要引入资源位置的 <string> 或者 url(url)。 这个 URL 可以是绝对路径或者相对路径。 要注意的是这个 URL 不需要指明一个文件; 可以只指明包名,然后合适的文件会被自动选择 (e.g. chrome://communicator/skin/).

link 和 @import 都能导入一个样式文件,它们有什么区别嘛?

  • 从属关系区别:link 是 HTML 标签,除了能导入 CSS 外,还能导入别的资源,比如图片、脚本和字体等;而 @import 是 CSS 的语法,只能用来导入 CSS;
  • 加载顺序区别:link 导入的样式会在页面加载时同时加载,@import 导入的样式需等页面加载完成后再加载;
  • 兼容性区别:link 没有兼容性问题,@import 不兼容 ie5 以下;
  • DOM可控性区别:link 可以通过 JS 操作 DOM 动态引入样式表改变样式,而@import不可以。
  • 权重区别:link引入的样式权重大于@import引入的样式。link可以通过rel="alternate stylesheet"指定候选样式

@supports

@supports 命令用来判断浏览器是否支持某项CSS功能,可以结合 not、and 和 or 操作符进行后续的操作。

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

另一个例子。

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

@property

@property 是用来注册一个变量的,该变量是一个 CSS Houdini 中的变量,但它的使用和 CSS 中的自定义属性(CSS变量)是一样的,不同的是注册方式:

// Chrome 78+
// 需要在 JavaScript脚本中注册
CSS.registerProperty({
    'name': '--custom-property-name',
    'syntax': '<color>',
    'initialValue': 'black',
    'inherits': false
})

// Chrome 85+
// 在CSS文件中注册
@property --custom-property-name {
    'syntax': '<color>',
    'initialValue': 'black',
    'inherits': false
}

他的最大特色之一就是可以指定已注册的 CSS 变量的类型、初始值,是否可继承。

@container

Una Kravets 在 Google I/O 开发大会上就分享了容器查询 @container ,她把它称为新的响式布局所需特性之一:

@scope

@layer

打印样式

基本用法

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

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

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

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

p { margin: 1em 0; }

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

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

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

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

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

分页符

分页符属性用来设置页面的分页(即另起一页),共有三个相关属性。

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

这三个属性的值都是两个:always(生效)和avoid(避免)。

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属性设置跨页前的行数少于多少行时,所有行都移到下一页打印。

p {
  orphans: 3;
}

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

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

p {
  widow: 2;
}

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

@page

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

@page {
  margin: 2cm;
}

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

@page:first {
  margin: 0;
}

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

技巧

(1)重复表格的表头

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

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

上面代码中,如果表格跨页,表头的CityPopulation字段会在每一页都打印出来。

(2)打印链接的网址

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

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

嵌套

使用过 CSS 处理器的同学,应该用过嵌套来组织自己的代码,庆幸的是,W3C 也在讨论和定义CSS中的嵌套规则。目前两种规则:

foo {
    color: red;
    @nest bar {
        color: green;
    }
}

// 或者 
foo {
    color: red;
    & bar {
        color: green;
    }
}

// 都等同于
foo {
    color: red;
}
foo bar {
    color: green;
}

也可以和媒体查询 @media 相互嵌套:

article {
  color: darkgray;
  & > a {
    color: var(--link-color);
  }
}

code > pre {
  @media (hover) {
    &:hover {
      color: hotpink;
    }
  }
}

code > pre {
  @media (hover) {
    @nest &:hover {
      color: hotpink;
    }
  }
}

article {
  @nest section:focus-within > & {
    color: hotpink;
  }
}

main {
  padding: var(--space-sm);
  @media (width >= 540px) { & {
    padding: var(--space-lg);
  }}
}

除了 @nest 之外还有 @apply 。你可能在一些前端的框架或构建器中看到过 @apply。它有点类似于 SCSS 中的混合宏 @mixin@extend :

:root {
    --brand-color: red;
    --heading-style: {
        color: var(--brand-color);
        font-family: cursive;
        font-weight: 700;
    }
}

h1 {
    --brand-color: green;
    @apply --heading-style;
}

值和单位

CSS 的声明是由属性和值组成的,而值的类型有许多种:

  • 数值:长度值 ,用于指定例如元素 width、border-width、font-size 等属性的值;
  • 百分比:可以用于指定尺寸或长度,例如取决于父容器的 width、height 或默认的 font-size;
  • 颜色:用于指定 background-color、color 等;
  • 坐标位置:以屏幕的左上角为坐标原点定位元素的位置,比如常见的 background-position、top、right、bottom 和 left 等属性;
  • 函数:用于指定资源路径或背景图片的渐变,比如 url()、linear-gradient() 等;

而还有些值是需要带单位的,比如 width: 100px,这里的 px 就是表示长度的单位,长度单位除了 px 外,比较常用的还有 em、rem、vw/vh 等。

px

px 表示的是 CSS 中的像素,在 CSS 中它是绝对的长度单位,也是最基础的单位,其他长度单位会自动被浏览器换算成 px。但是对于设备而言,它其实又是相对的长度单位,比如宽高都为 2px,在正常的屏幕下,其实就是 4 个像素点,而在设备像素比(devicePixelRatio) 为 2 的 Retina 屏幕下,它就有 16 个像素点。所以屏幕尺寸一致的情况下,屏幕分辨率越高,显示效果就越细腻。

设备像素(Device pixels)

设备屏幕的物理像素,表示的是屏幕的横纵有多少像素点;和屏幕分辨率是差不多的意思。比如分辨率 1920×1080 意味着水平方向含有 1920 个像素数,垂直方向含有 1080 个像素数。

设备像素比(DPR)

设备像素比表示 1 个 CSS 像素等于几个物理像素。

计算公式:DPR = 物理像素数 / 逻辑像素数;

在浏览器中可以通过 window.devicePixelRatio 来获取当前屏幕的 DPR。

像素密度(DPI/PPI)

像素密度也叫显示密度或者屏幕密度,缩写为 DPI(Dots Per Inch) 或者 PPI(Pixel Per Inch)。从技术角度说,PPI 只存在于计算机显示领域,而 DPI 只出现于打印或印刷领域。

计算公式:像素密度 = 屏幕对角线的像素尺寸 / 物理尺寸

设备独立像素(DIP)

DIP 是特别针对 Android设备而衍生出来的,原因是安卓屏幕的尺寸繁多,因此为了显示能尽量和设备无关,而提出的这个概念。它是基于屏幕密度而计算的,认为当屏幕密度是 160 的时候,px = DIP。

计算公式:dip = px * 160 / dpi

em

em 是 CSS 中的相对长度单位中的一个。它有 2 层意思:

  • 在 font-size 中使用是相对于父元素的 font-size 大小,比如父元素 font-size: 16px,当给子元素指定 font-size: 2em 的时候,经过计算后它的字体大小会是 32px。

  • 在其他属性中使用是相对于自身的字体大小,如 width/height/padding/margin 等;这里比较混淆的是,如果font-size也使用em,两者的计算基点是不一样的。

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

    上面代码中,font-size是基于父元素计算的,如果父元素的字体大小是16px,那么font-size就是32pxpadding是基于font-size计算的,由于h1font-size32px,所以padding就是32px

我们都知道每个浏览器都会给 HTML 根元素 html 设置一个默认的 font-size,而这个值通常是 16px。这也就是为什么 1em = 16px 的原因所在。

em 在计算的时候是会层层计算的,比如:

<div>
    <p></p>
</div>

div { font-size: 2em; }
p { font-size: 2em; }

对于如上一个结构的 HTML,由于根元素 html 的字体大小是 16px,所以 p 标签最终计算出来后的字体大小会是 16 * 2 * 2 = 64px。

rem

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

那么,何时使用rem,何时使用em呢?一个规则是字体大小font-size属性使用rem,其他必须等比例缩放的属性使用em。下面是一个例子。

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;
  }
}

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

vw/vh

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

vmin表示vhvw之中较短的那个单位,vmax则表示较长的那个单位。

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

h1 {
  font-size: 20vmin;
}

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

在 JS 中 100vw = window.innerWidth,100vh = window.innerHeight。

ch

ch表示多少个字符。

width: 40ch;

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

calc()

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

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

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

attr()

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

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

min() 和 max()

  • min() 函数会从多个参数(或表达式)中返回一个最小值作为CSS属性的值,即 使用 min() 设置最大值,等同于 max-width
  • max() 函数会从多个参数(或表达式)中返回一个最大值作为CSS属性的值,即 使用max()设置最小值,等同于 min-width

clamp()

clamp()min() 以及 max()略有不同,它将返回一个区间值,即 在定义的最小值和最大值之间的数值范围内的一个中间值。该函数接受三个参数:

  • 最小值(MIN
  • 中间值(VAL),也称首选值
  • 最大值(MAX

clamp(MIN, VAL, MAX),这三个值之间的关系(或者说取值的方式):

  • 如果 VALMINMAX 之间,则使用 VAL 作为函数的返回值
  • 如果 VAL 大于 MAX ,则使用 MAX 作为函数的返回值
  • 如果 VAL 小于 MIN ,则使用 MIN 作为函数的返回值

比如下面这个示例:

.element { 
    /** 
    * MIN = 100px 
    * VAL = 50vw ➜ 根据视窗的宽度计算 
    * MAX = 500px 
    **/ 
    width: clamp(100px, 50vw, 500px); 
}

就这个示例而言,clamp() 函数的计算会经历以下几个步骤:

.element { 
    width: clamp(100px, 50vw, 500px); 

    /* 50vw相当于视窗宽度的一半,如果视窗宽度是760px的话,那么50vw相当等于380px*/ 
    width: clamp(100px, 380px, 500px); 

    /* 用min()和max()描述*/ 
    width: max(100px, min(380px, 500px)) 

    /*min(380px, 500px)返回的值是380px*/ 
    width: max(100px, 380px) 

    /*max(100px, 380px)返回的值是380px*/ 
    width: 380px; 
}

颜色体系

我们现在描述颜色都是在sRBG 色值空间,而颜色色值空间是一个复杂的体系,除了 sRGB 之外还有其他的色值空间,比如说 LCH

正如上图所示,LCH 颜色空间的颜色数量要比 sRGB 颜色空间的多,而且描述的颜色更为细腻。

可以在color() 函数中指定颜色空间:

#color-function {
  --rad-pink: color(display-p3 1 0 1);
  --rad-pink: color(lab 50% 150 -50);
  --rad-pink: color(srgb 100% 0% 50%);
}

注意,使用了color() 函数指定颜色空间除了要考虑该函数支持度(浏览器的兼容性)还需要考虑硬件设备对颜色空间的支持度。

在 CSS 中可以借助媒体查询 @media 来做相应的判断,比如下面的示例,如果终端设备支持的话,就会采用指定颜色空间的色值:

@media (dynamic-range: high) {
  .neon-pink {
    --neon-glow: color(display-p3 1 0 1);
  }
  
  .neon-green {
  	--neon-grow: color(display-p3 0 1 0);
  }
}

颜色关键字

颜色关键字(color keywords)是不区分大小写的标识符,它表示一个具体的颜色,比如 white(白),黑(black)等;需要注意的是如果声明的时候的颜色关键字是错误的,浏览器会忽略它。

CSS2.1 设置了16个基本的颜色,CSS 3 又增加了131个。

transparent 关键字

transparent 关键字表示一个完全透明的颜色,即该颜色看上去将是背景色。从技术上说,它是带有 alpha 通道为最小值的黑色,是 rgba(0,0,0,0) 的简写。

currentColor 关键字

currentColor 会取当前元素继承父级元素的文本颜色值或声明的文本颜色值,即 computed 后的 color 值。

RGB[A] 颜色

RGB[A] 颜色是由 R(red)-G(green)-B(blue)-A(alpha) 组成的色彩空间。

十六进制符号

日常生活中,我们使用的计数方法一般是 decimals,或十进制,即使用数字 0 到 9 来表示。 而 Hexadecimals(或 hex)基于 16 位数字, 它包括 16 种不同字符。 像十进制一样,0-9 的符号代表 0 到 9 的值。 然后,A、B、C、D、E、F 代表 10 至 15 的值。

button {
    color: #ff0000aa;
    color: #f00a;
}

RGB 中的每种颜色的值范围是 00~ff,值越大表示颜色越深。所以一个颜色正常是 6 个十六进制字符加上 # 组成,比如红色就是 #ff0000。

如果 RGB 颜色需要加上不透明度,那就需要加上 alpha 通道的值,它的范围也是 00~ff,比如一个带透明度为 67% 的红色可以这样写 #ff0000aa。

使用十六进制符号表示颜色的时候,都是用 2 个十六进制表示一个颜色,如果这 2 个字符相同,还可以缩减成只写 1 个,比如,红色 #f00;带 67% 不透明度的红色 #f00a。

函数符

button {
    color: rgba(255, 0, 0, 0.67);
    color: rgb(100% 0% 0% / 67%);
}

当 RGB 用函数表示的时候,每个值的范围是 0~255 或者 0%~100%,所以红色是 rgb(255, 0, 0), 或者 rgb(100%, 0, 0)。

如果需要使用函数来表示带不透明度的颜色值,值的范围是 0~1 及其之间的小数或者 0%~100%,比如带 67% 不透明度的红色是 rgba(255, 0, 0, 0.67) 或者 rgba(100%, 0%, 0%, 67%),其中 0 代表完全透明,1 代表完全不透明。

需要注意的是 RGB 这 3 个颜色值需要保持一致的写法,要嘛用数字要嘛用百分比,而不透明度的值的可以不用和 RGB 保持一致写法。比如 rgb(100%, 0, 0) 这个写法是无效的;而 rgb(100%, 0%, 0%, 0.67) 是有效的。

在第 4 代 CSS 颜色标准中,新增了一种新的函数写法,即可以把 RGB 中值的分隔逗号改成空格,而把 RGB 和 alpha 中的逗号改成 /,比如带 67% 不透明度的红色可以这样写 rgba(255 0 0 / 0.67)。另外还把 rgba 的写法合并到 rgb 函数中了,即 rgb 可以直接写带不透明度的颜色。

HSL[A] 颜色

HSL[A] 颜色是由色相(hue)-饱和度(saturation)-亮度(lightness)-不透明度组成的颜色体系。

button {
    color: hsla(0, 100%, 50%, 67%);
    color: hsl(0deg 100% 50% / 67%);
}

  • 色相(H)是色彩的基本属性,就是平常所说的颜色名称,如红色、黄色等。 以颜色光谱为例,光谱左边从红色开始,移动到中间的绿色,一直到右边的蓝色,色相值就是沿着这条线的取值。 在 hsl() 里面,色相用色环来代替光谱,色相值就是色环里面的颜色对应的从 0 到 360 度的角度值。
  • 饱和度(S)是指色彩的纯度,也就是颜色里灰色的占比。 饱和度越高则灰色占比越少,色彩也就越纯;反之则完全是灰色。饱和度的取值范围是表示灰色所占百分比的 0 至 100。
  • 亮度(L)决定颜色的明暗程度,也就是颜色里白色或者黑色的占比。 其中,100% 的亮度表示纯白色, 0% 的亮度则表示纯黑色;而 50% 的亮度就表示在色相中选取的颜色。
  • 不透明度(A),取 0 或 1及之间的小数;

写法上可以参考 RGB 的写法,只是参数的值不一样。

小提示:在 Chrome DevTools 中可以按住 shift + 鼠标左键可以切换颜色的表示方式。

配色方案

颜色理论研究的都是颜色的本质,至于颜色搭配,最终靠的还是个人感觉。说到底,Choosing colors is art, not science。

Flat UI 色表

最简单的方法是,从一个给定的色表选择颜色。

配色的经验法则

base16

An architecture for building themes

Adobe Color CC

选一个颜色,推荐对应的配色。

互补色

在网站设计里,颜色能让内容更醒目,能调动情绪,从而创造舒适的视觉体验。 不同的颜色组合对网站的视觉效果影响很大,精妙的设计都需要适宜的颜色来美化页面内容。

色环是我们认识颜色关系的好工具。它是一个近色相邻、异色相离的圆环。 当两个颜色恰好在色环的两端时,这两个颜色就互为补色。 两个互为补色的颜色会在混合后变成灰色。 然而,补色搭配能形成强烈的视觉对比效果。

颜色搭配是提起用户兴趣或吸引用户注意的重要方式之一。 但我们不应让颜色作为传达重要信息的唯一方式,因为视觉障碍用户可能无法像其他人一样看出其中的含义。

三次色

电脑显示器和各类屏幕都是基于颜色叠加的模型:将红(R)、绿(G)、蓝(B)三原色的色光以不同的比例相加,就可以产生各种色彩光。 这在现代色彩理论中叫作三原色光模式(RGB Color Model)。 红色(R)、绿色(G)和蓝色(B)叫作三原色。 如果把两种原色相加,就可以产生二次色:蓝绿(G+B)、品红(R+B)和黄色(R+G)。这些二次色恰好是在合成它们时未使用的原色的补色,即在色环中位于两端。 例如,品红色是红色和蓝色相加产生,它是绿色的补色。

三次色是由原色和二次色相加产生的颜色, 例如,在 RGB 颜色模型中,红色(原色)和黄色(二次色)相加产生橙色(三次色)。 将这六种颜色中相邻的颜色相加,便产生了十二色色环。

设计里面有很多种颜色搭配方法。 涉及到三次色的一种配色方法是分裂补色搭配法。 选定主色之后,在色环上选择与它的补色相邻的两种颜色与之搭配。 此种搭配既有对比,又不失和谐。

颜色函数

更为强大的是 CSS 颜色模块 Level 5 版本,对颜色函数能力做了进一步的扩展。比如,可以在 rgb()hsl()hwb()lab()lch() 函数基础上添加 from 关键词,实现基于一个颜色的基础上,只对某个参数做调整:

rgb() = rgb([from <color>]? <percentage>{3} [ / <alpha-value> ]? ) | rgb([from <color>]? <number>{3} [ / <alpha-value> ]? ) 
hsl() = hsl([from <color>]? <hue> <percentage> <percentage> [ / <alpha-value> ]? ) 
hwb() = hwb([from <color>]? <hue> <percentage> <percentage> [ / <alpha-value> ]? ) 
lab() = lab([from <color>]? <percentage> <number> <number> [ / <alpha-value> ]? ) 
lch() = lch([from <color>]? <percentage> <number> <hue> [ / <alpha-value> ]? )

来看一个 hsl() 的示例:

img

上图展示的是,基于 --theme-primary (原色,即 hsl(274, 61%, 50%))颜色只对l 参数做调整,从 50% 调整到 30% ,从而获得一个新的颜色,即 hsl(274, 61%, 30%) 。使用这样的方式,我们就可以很轻易的获取基于某个颜色参数改变得来的颜色面板:

img

除此之外, 颜色模块 Level 5 版本还新增了一些新的函数用来描述颜色,比如 color-mix()color-contrast()color-adjust() 等:

.color-mix {
  --pink: color-mix(red, white);

  --brand: #0af;
  --text1: color-mix(var(--brand) 25%, black);
  --text2: color-mix(var(--brand) 40%, black);
}

.color-contrast {
  color: color-contrast(
    var(--bg)
    vs
    black, white
  );
}

--text-on-bg: color-contrast(
  var(--bg-blue-1)
  vs
  var(--text-subdued),
  var(--text-light),
  var(--text-lightest)
);

.color-adjust {
  --brand: #0af;
  --darker: color-adjust(var(--brand) lightness -50%);
  --lighter: color-adjust(var(--brand) lightness +50%);
}

简单介绍一下这几个颜色函数。其中 color-mix() 函数接受两个 <color> 值,并在给定的颜色空间中以指定的数量返回它们混合的结果。如果没有特殊说明,否则在 lch() 颜色空间中进行混合。比如下图所展示的就是 在lch() 颜色空间(默认)中将 redyellow 混合,每个 lch 通道的红色值占65%,黄色值占 35%,即 color-mix(red yellow 65%)

img

color-contrast() 函数很有意思,它可以帮助我们提高Web可访问性方面的能力。其主要作用是获取一个颜色值,并将其与其他颜色值的列表进行比较,从列表中选择对比度最高的一个。

img

比如 color-contrast(white vs red, white, green) ,分别会拿 redwhitegreenwhite 对比,其中 greenwhite 对比度最高,最终会取 green 颜色:

img

color-adjust() 函数可用来在给定颜色空间中通过指定的变换函数对该颜色进行调整,除非另有规定,否则调整是在 lch() 色彩空间中进行的。比如 color-adjust(#0af lightness +50%) 是颜色 #0aflch 颜色空间中高度增加 50%

CSS 混合模式

CSS 混合模式是个很有意思的特性,目前主要有 mix-blend-modebackground-blend-mode 两个属性,前者是用于多个元素的合成,后者是用于多个背景的合成。使用它们可以实现一些特殊的效果,比如类似 Photoshop 中的滤镜效果:

采用混合模式特性,我们可以轻易的实现产品图换色的效果。

CSS 变量

变量的声明

CSS 变量(CSS variable)又叫做"CSS 自定义属性"(CSS custom properties)。

声明变量的时候,变量名前面要加两根连词线(--)。各种值都可以放入 CSS 变量。

:root{
  --main-color: #4d4e53;
  --main-bg: rgb(255, 255, 255);
  --logo-border-color: rebeccapurple;

  --header-height: 68px;
  --content-padding: 10px 20px;

  --base-line-height: 1.428571429;
  --transition-duration: .35s;
  --external-link: "external link";
  --margin-top: calc(2vh + 20px);
}

变量名大小写敏感,--header-color--Header-Color是两个不同变量。

如果变量值是一个字符串,可以与其他字符串拼接。

--bar: 'hello';
--foo: var(--bar)' world';

如果变量值是数值,不能与数值单位直接连用。

.foo {
  --gap: 20;
  margin-top: var(--gap)px;		/* 无效 */
}

必须使用calc()函数,将它们连接。

.foo {
  --gap: 20;
  margin-top: calc(var(--gap) * 1px);
}

如果变量值带有单位,就不能写成字符串。

.foo {
  --foo: '20px';		/* 无效 */
  font-size: var(--foo);
}

var() 函数

var()函数用于读取变量。

a {
  color: var(--foo);
  text-decoration-color: var(--bar);
}

var()函数还可以使用第二个参数,表示变量的默认值。如果该变量不存在,就会使用这个默认值。第二个参数不处理内部的逗号或空格,都视作参数的一部分。

.someElement {
	color: var(--foo, #7F583F);
	font-family: var(--main-font, "lucida grande" , tahoma, Arial);
}

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

var()函数还可以用在变量的声明。

:root {
  --primary-color: red;
  --logo-text: var(--primary-color);
}

注意,变量值只能用作属性值,不能用作属性名。

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

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

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

作用域

自定义属性可以是全局的,也可以是局部的。在:root选择器里面定义的,就是全局变量,可以在任何其他选择器里面读取。而在其他选择器里面定义,就是局部变量,只能在该选择器里面读取。这就是说,变量的作用域就是它所在的选择器的有效范围。

同一个 CSS 变量,可以在多个选择器内声明。读取的时候,优先级最高的声明生效。这与 CSS 的"层叠"(cascade)规则是一致的。

无效变量

CSS 自定义属性中的无效变量是很有用的一个特性,它可以实现 1 (真)和 0 (假)的开关切换效果。

定义:当一个自定义属性的值是 initial 时,var() 函数不能使用它进行替换。除非指定了一个有效的回退值,否则会使声明在计算值时无效。

在 CSS 中注册自定义属性时是使用 -- 来注册的,可以给已注册的自定义属性赋值,包括空字符串,但其中有一个细节非常有意思:

:root {
    --invalid:; // 注意冒号和分号之间无空格符,也无任何字符
    --valid: ;  // 注意冒号和分号之间只有一个空格符
}

其中,--invalid 自定义属性被称为无效变量,而 --valid 自定义属性是一个有效变量。使用上面这种方式来区分有效和无效变量对于开发者而言,可读性极差,而且有些文本编辑器可能会对代码按自己配置的规格格式化,有可能会造成 --invalid:; 变成 --invalid: ; (有空格)。为此,一般使用关键词 initial来显式声明一个自定义属性为无效变量:

:root {
    --invalid: initial; 		// 无效变量
    --valid: ;					// 有效变量
}

如果不使用 var() 函数调用已注册的自定义属性的话,那么对于已注册的自定义属性而言,不会起任何作用。而 var() 函数有两个参数,第一个参数就是自定义属性,第二个参数是备用值。当第一个参数是个无效值时,会采用第二个参数。正因如此,对于已注册的无效自定义属性(即无效变量),比如 上面代码中的 --invalid 。那么 var() 没有提供备用值(第二个参数),则会使 CSS 样式规则(声明)在计算值时无效:

:root {
    --invalid: initial; // 无效变量
    --valid: ;          // 有效变量
}

foo {
    background-color: var(--invalid); // 未提供备用值,则background-color 计算值无效
    color: var(--invalid, red)        // 提供了备用值,--invalid是无效变量,则会采用备用值 red
}

为了在使用 CSS 自定义属性中的无效变量让开发者更易于理解,@Lea Verou《The –var: ; hack to toggle multiple values with one custom property》引入了类似于Switch的概念,即:

:root {
    --ON: initial; // 无效变量,相当于开启 var()的备用值
    --OFF:;        // 有效变量,相当于关闭 var()的备用值
}

这样一来,我们在做 UI 不同状态切换时,就只需要对 --ON--OFF 的切换。比如 @Lea Verou 在文章中提供的示例

:root {
    --ON: initial;
    --OFF: ;
}

button {
    --is-raised: var(--OFF);

    border: 1px solid var(--is-raised, rgb(0 0 0 / 0.1));
    background: var(
        --is-raised,
        linear-gradient(hsl(0 0% 100% / 0.3), transparent)
    )
        hsl(200 100% 50%);
    box-shadow: var(
        --is-raised,
        0 1px hsl(0 0% 100% / 0.8) inset,
        0 0.1em 0.1em -0.1em rgb(0 0 0 / 0.2)
    );
    text-shadow: var(--is-raised, 0 -1px 1px rgb(0 0 0 / 0.3));
}

button:hover {
    --is-raised: var(--ON);
}

button:active {
    box-shadow: var(--is-raised, 0 1px 0.2em black inset);
}

应用无障碍

使用自定义 CSS 让元素仅对屏幕阅读器可见

如果我们需要在页面中添加一些只对屏幕阅读器可见的内容时,CSS 可以提升页面的可访问性。当信息以可视化形式(如:图表)展示,而屏幕阅读器用户需要一种替代方式(如:表格)来获取信息时,就会出现这种情况。CSS 被用来将这些仅供屏幕阅读器使用的信息定位到浏览器可见区域之外。

.sr-only {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  top: auto;
  overflow: hidden;
}

display: none;visibility: hidden;会把内容彻底隐藏起来,对于屏幕阅读器也不例外。

如果把值设置为 0px,如width: 0px; height: 0px;,意味着让元素脱离文档流,这样做也会让元素被屏幕阅读器忽略。

使用高对比度文本提高可读性

低对比度的前景色与背景色会使文本难以阅读。足够的对比度可以提高内容的可读性,但是怎样的对比度才算是 “足够” 的?

Web 内容无障碍指南(WCAG)建议正常文本的对比度至少为 4.5 : 1。对比度是通过比较两种颜色的相对亮度值来计算的,其范围是从相同颜色的 1 : 1(无对比度)到白色与黑色的最高对比度 21 : 1。网上有很多可以帮助你计算对比度的工具。

通过使用充足的对比度避免色盲问题

颜色是可视化设计的重要组成部分,但是使用颜色也引入了两个可访问性问题。首先,不能仅仅使用颜色作为传达重要信息的唯一方式,因为屏幕阅读器无法获取这些信息。其次,前景色与背景色需要有足够的对比度,这样色盲用户才可以识别它们。

我们用文本备用方案解决了第一个问题。我们使用对比度检测工具解决了第二问题。

通过仔细选择传达信息的颜色来避免色盲问题

色盲的形式有很多种,它的表现可以从对特定波长光波的感知度较低,到几乎无法看到颜色。最常见的形式是对绿色的低感知度。

一些在线颜色拾取器有色盲模拟功能,可以模拟颜色在不同形式色盲的视觉中的呈现结果,它们和在线对比度检查器一样,都是很好的工具。

通过给元素添加 accesskey 属性来让用户可以在链接之间快速导航

HTML 提供accesskey属性,用于指定激活标签或者使标签获得焦点的快捷键,这可以使键盘用户的导航更加有效。

HTML5 允许在任何标签上使用这个属性。该属性对于交互类标签(如链接、按钮、表单控件等)十分有用。

<button accesskey="b">Important Button</button>

使用 tabindex 将键盘焦点添加到元素中

HTML 的tabindex属性有三个不同与标签焦点的功能。当它在标签上时,表示标签可以获得焦点。它的值可以是零、负整数及正整数,并决定了标签的行为。

当用户在页面中使用 tab 键时,有些标签,如:链接、表单控件,可以自动获得焦点。它们获得焦点的顺序与它们出现在文档流中的顺序一致。我们可以通过将tabindex属性值设为 0,来给其他标签赋予相同的功能。

<div tabindex="0">I need keyboard focus!</div>

tabindex属性值为负整数(通常为 -1)的标签也是有焦点的,只是不可以通过 tab 键来获得焦点。

tabindex属性还可以指定标签的 tab 键顺序,将它的值设置为大于或等于 1 就可以实现这个功能。tabindex属性值为 1 的标签将首先获得键盘焦点,然后焦点将按照指定的tabindex的值(如:2,3 等)的顺序进行移动,直到回到默认的或tabindex值为 0 的标签上,如此循环。

<div tabindex="1">I get keyboard focus, and I get it first!</div>

<div tabindex="2">I get keyboard focus, and I get it second!</div>

需要注意的是,当按照这种方式设置 tab 键顺序时,将会覆盖默认的顺序(标签在文档流中出现的顺序)。这可能会令那些希望从页面顶部开始导航的用户感到困惑。这个技术在某些情况下可能是必要的,但是对可访问性而言,在应用时要十分小心。

“流”又叫文档流,是css的一种基本定位和布局机制。流是html的一种抽象概念,暗喻这种排列布局方式好像水流一样自然自动。“流体布局”是html默认的布局机制,如你写的html不用css,默认自上而下(块级元素如div)从左到右(内联元素如span)堆砌的布局方式。

盒模型

w3c标准模型: box-sizing: content-box 此模式下,元素的宽度计算为content的宽度。

IE模型: box-sizing: border-box 此模式下,元素的宽度计算为border+padding+content的宽度总和。

视觉格式化模型

视觉格式化模型(Visual formatting model)是用来处理和在视觉媒体上显示文档时使用的计算规则。CSS 中一切皆盒子,而视觉格式化模型简单来理解就是规定这些盒子应该怎么样放置到页面中去。

盒子类型由 display 决定,同时给一个元素设置 display 后,将会决定这个盒子的 2 个显示类型(display type):

  • outer display type(对外显示)(外在盒子):决定了该元素本身是如何布局的,即参与何种格式化上下文;外在盒子负责结构布局。
  • inner display type(对内显示)(内在盒子):其实就相当于把该元素当成了容器,里面包裹着文本或者其他子元素。规定了其内部子元素是如何布局的、参与何种格式化上下文;内在盒子负责内容显示。

outer display type

对外显示方面,盒子类型可以分成 2 类:block-level box(块级盒子) 和 inline-level box(行内级盒子)。

  • 块级盒子:display 为 block、list-item、table、flex、grid、flow-root 等;
  • 行内级盒子:display 为 inline、inline-block、inline-table、table-cell 等;

所有块级盒子都会参与 BFC,呈现垂直排列;而所有行内级盒子都参会 IFC,呈现水平排列。

block

  • 占满一行,默认继承父元素的宽度;多个块元素将从上到下进行排列;
  • 设置 width/height 将会生效;
  • 设置 padding 和 margin 将会生效;

inline

  • 不会占满一行,宽度随着内容而变化;多个 inline 元素将按照从左到右的顺序在一行里排列显示,如果一行显示不下,则自动换行;
  • 设置 width/height 将不会生效;
  • 设置竖直方向上的 padding 和 margin 将不会生效;

inline-block

  • 是行内块元素,不单独占满一行,可以看成是能够在一行里进行左右排列的块元素;
  • 设置 width/height 将会生效;
  • 设置 padding 和 margin 将会生效;

inner display type

对内方面,其实就是把元素当成了容器,里面包裹着文本或者其他子元素。container box 的类型依据 display 的值不同,分为 4 种:

  • block container:建立 BFC 或者 IFC;
  • flex container:建立 FFC;
  • grid container:建立 GFC;
  • ruby container:接触不多,不做介绍。

值得一提的是如果把 img 这种替换元素(replaced element)申明为 block 是不会产生 container box 的,因为替换元素比如 img 设计的初衷就仅仅是通过 src 把内容替换成图片,完全没考虑过会把它当成容器。

内联盒模型

内联元素是指外在盒子是内联盒子的元素。从表现来说,内联元素的典型特征就是可以和文字在一行显示。文字也是内联元素。图片、按钮、输入框、下拉框等替换元素也是内联元素。内联盒模型是指内联元素包含的几个盒子:

  • 内容区域:本质上是字符盒子。在浏览器中,文字选中状态的背景色就是内容区域。
  • 内联盒子:内联盒子就是指元素的外在盒子是内联的,会和其他内联盒子排成一行。
  • 行框盒子:由内联元素组成的每一行都是一个行框盒子。如果一行里面没有内联元素如一个空的div标签,则不会形成行框盒子。行框盒子由一个个内联盒子组成,如果换行,那就是两个行框盒子。比如一个包含了很多字符的换行的的p标签,每一行都存在一个行框盒子。值得注意的是,如果给元素设置display: inline-block,则创建了一个独立的行框盒子。line-height是作用在行框盒子上的,并最终决定高度。
  • 包含盒子:就是包含块。多行文字组成一个包含块,一个包含块有若干个行框盒子。
  • 幽灵空白节点:内联元素的每个行框盒子前面有一个“空白节点”,这个“空白节点”不占据任何宽度,无法选中获取,但是又实实在在存在,表现就如同文本节点一样(用字母x模拟幽灵空白节点)。

替换元素

替换元素是指内容可以替换的元素,实际上就是content box可以被替换的元素。如存在src=""属性的<img> <audio> <video> <iframe>元素和可以输入文本的<input> <select> <textarea>元素等。

所有替换元素都是内联元素,默认display属性是inlineinline-block(除了input[type="hidden"]默认display: none;)。

替换元素有自己默认的样式、尺寸(根据浏览器不同而不同),而且其vertical-align属性默认是bottom(非替换元素默认值是baseline)。

width/height 属性

你可以使用 CSS 里的 width/height 属性来指定元素的宽度。 属性值可以是相对单位(比如 em),绝对单位(比如 px),或者包含块(父元素)宽度的百分比。

widthheight的默认值都是auto

  • 对于块级元素,流体布局之下width: auto自适应撑满父元素宽度。这里的撑满并不同于width: 100%的固定宽度,而是像水一样能够根据margin不同而自适应父元素的宽度。
  • 对于内联元素,width: auto则呈现出包裹性,即由子元素的宽度决定。
  • 无论内联元素还是块级元素,height: auto都是呈现包裹性,即高度由子级元素撑开。

注意父元素height: auto会导致子元素height: 100%百分比失效。

css的属性非常有意思,正常流下,如果块级元素的width是个固定值,marginauto,则margin会撑满剩下的空间;如果margin是固定值,widthauto,则width会撑满剩下的空间。这就是流体布局的根本所在。

content 属性

对于非替换元素如div,其content就是div内部的元素。 而对于替换元素,其content就是可替换部分的内容。

content属性主要用于伪元素:before/:after中。

padding 属性

padding: 30px 20px 40px 100px;	/* top right bottom left */
padding: 30px 20px 40px;		/* 30px 20px 40px 20px */
padding: 30px 40px;				/* 30px 40px 30px 40px */
padding: 30px;					/* 30px 30px 30px 30px */

padding 区域有背景色,且和内容区相同。

元素真正的内容的宽高只是content box的宽高,而line-height属性是不作用于padding的。

padding不可为负值,但是可以为百分比值。为百分比时水平和垂直方向的padding都是相对于父级元素宽度计算的。

padding配合background-clip属性,可以制作一些特殊形状。

border 属性

border: 2px solid red;		/* border-width border-style border-color */

border-width属性的默认值是3px,是为了照顾小弟border-style: double,你懂的。值得注意的是,border-color默认是跟随字体的颜色,相当于默认设置了border-color: currentColor一样。

border-style 属性

border-image 属性

/* 边框图片的路径 */
border-image-source: url("images/border.png");

/* 图片边框的裁剪(宽高为27)*/
border-image-slice: 27;

/* 图片边框的宽度 */
border-image-width: 27px;

/* 边框图片的平铺 */
/* repeat :正常平铺 但是可能会显示不完整 */
/* round: 平铺 但是保证 图片完整 */
/* stretch: 拉伸显示 */
border-image-repeat: stretch;

/* 综合属性 */
border-image: url("images/border.png") 27/20px round;

border-radius 属性

CSS 属性 border-radius 允许你设置元素的外边框圆角。当使用一个半径时确定一个圆形,当使用两个半径时确定一个椭圆。这个(椭)圆与边框的交集形成圆角效果。

border-radius: 60px/120px;		/* 水平半径/垂直半径 */
border-radius: 20px 60px 100px 140px;	/* 从左上开始,顺时针赋值。如果当前角没有值,取对角的值 */
border-radius: 60px;

该属性是一个 简写属性,是为了将这四个属性 border-top-left-radius、border-top-right-radius、border-bottom-right-radius,和 border-bottom-left-radius 简写为一个属性。

  • length:定义圆形半径或椭圆的半长轴,半短轴。负值无效。
  • percentage:使用百分数定义圆形半径或椭圆的半长轴,半短轴。水平半轴相对于盒模型的宽度;垂直半轴相对于盒模型的高度。负值无效。

box-shadow 属性

box-shadow 属性用来给元素添加阴影,设置边框阴影不会改变盒子的大小,即不会影响其兄弟元素的布局,但阴影可能会覆盖其他元素。

box-shadow: inset 15px 21px 48px -2px #666;

box-shadow 属性的阴影依次由下面这些值描述:

  • offset-x & offset-y

    • 非inset:

      • 正负:阴影移动的方向。正值向右,负值向左;正值向下,负值向上。
      • 大小:阴影的水平与垂直偏移量,即将阴影从原位置移动距离。
    • inset:默认阴影在边框外,即阴影向外扩散。使用 inset 关键字会使得阴影落在盒子内部,这样看起来就像是内容被压低了。 此时阴影会在边框之内 (即使是透明边框)、背景之上、内容之下。

      • 正负:表示从哪条边出现阴影。正:左上,负:右下
      • 大小:表示阴影在某一侧的长度。spread-radius和 inset offset 值配合起来的最大值也不能超出元素的尺寸,因为如果超出元素,那么设置这个元素就没有意义了,因为它已经看不见了
  • blur-radius 模糊半径。值越大,模糊面积越大,阴影就越大越淡。 不能为负值。默认为0,此时阴影边缘锐利。例子:设置了一个20px的模糊半径,此时就会以四个方向的阴影边缘线为中心,左右扩展10px的区域,并对这部分进行高斯模糊,共同组成模糊的半径,即 20px,这样四个方向都会产生模糊效果。这里需要注意的是,阴影本质是在元素上方的,经过移动与模糊后,并且最终被裁剪掉。

  • spread-radius 阴影扩展半径,可选。取正值时,阴影扩大;取负值时,阴影收缩。默认为0,此时阴影与元素同样大。扩展二字就很能说明问题了,其实它就是扩展的阴影面积的大小。一开始,阴影的面积是等于元素的尺寸的,我们的模糊半径也只能从这个阴影的边缘两侧开启高斯模糊,当我们想修改这个阴影的面积时,就用到了spread-radius属性。spread-radius表示四个方向增大或者减小的尺寸,每一侧增加的扩展半径的值。

  • color:这个值可设可不设,但是因为用户代理不同,阴影的颜色的默认值也会不同,所以建议统一设置

可以通过逗号分隔每个 box-shadow 元素的属性来添加多个 box-shadow。

box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);

为什么叫box-shadow:

  • 表明这只是对盒子的阴影,跟盒子里面的内容没有关系,并不是真正意义的单侧光源阴影,从上面阴影的生成过程就已经就可以了解到,第一步就是生成等同box的阴影,并不会考虑盒子内部是否透明,内部透明是否也能产生阴影,因为阴影会根据盒子裁剪重合的部分。 这也就是它和drop-shadow的区别,后面的应用会具体比较描述。
  • 阴影的大小依据,是根据border-box的。对于inset,阴影也是从border内边缘扩展。

margin 属性

块级元素的垂直方向会发生margin合并,存在以下三种场景:

  • 相邻兄弟元素之间margin合并;
  • 父元素margin-top和子元素margin-top,父元素margin-bottom和子元素margin-bottom
  • 空块级元素自身的margin-topmargin-botom合并

要阻止父元素和子元素margin合并,可以:

  1. 把元素放到bfc中;
  2. 设置borderpadding阻隔margin
  3. 用内联元素(如文字)阻隔;
  4. 给父元素设定高度。

margin的百分比值跟padding一样,垂直方向的margin和水平方向上的一样都是相对于父元素宽度计算的。

margin: auto能在块级元素设定宽高之后自动填充剩余宽高。margin: auto自动填充触发的前提条件是元素在对应的水平或垂直方向具有自动填充特性,显然默认情况下块级元素的高度是不具备这个条件的。

display: block;
width: 200px;
margin: 0 auto;

auto的特性是,如果两侧都是auto,则两侧均分剩余宽度;如果一侧margin是固定的,另一侧是auto,则这一侧auto为剩余宽度。

垂直方向的margin也能实现垂直居中,但是需要元素在垂直方向具有自动填充特性,而这个特性可以利用position实现:

position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
width: 200px;
height: 200px;
margin: auto;

margin 为负值

表现

虽然margin可以应用到所有元素,但display属性不同时,表现也不同:

  1. block元素可以使用四个方向的margin值

    • 当static元素的margin-top/margin-left被赋予负值时,元素将被拉进指定的方向。设置margin-bottom/right为负数,元素并不会如你所想的那样向下/右移动,而是将后续的元素拖拉进来,覆盖本来的元素。
    • 在元素是流布局的时候,即元素width是默认的auto并且可以撑满一行的时候。通过设置margin为负值,能改变元素水平方向的尺寸,值为 width + 2 * |margin| 。原理是css权威指南上面提过的,就是无论你margin,paddding怎么变,最终都要等于父元素的宽高,比如父元素300px,子元素撑满父元素也是300px,此时子元素你设置margin-left: -100px,因为总宽度子元素总宽度要等与父元素300px,此时子元素要多宽加上-100才等于父元素的300px?说明子元素只有变为400px,400+(-100)才能等于父元素宽度
  2. inline元素使用上下方向的margin值无效

  3. inline-block使用上下方向的margin负值看上去无效

    注意:inline-block使用上下方向的margin负值只是看上去无效,这与其默认的vertical-align:baseline有关系,当垂直对齐的属性值为其他值时,则会显示不同的视觉效果

定位

  1. 定位元素(position不为static)覆盖其他元素的背景和内容

  2. 将relative属性值应用于inline元素,由于无法改变其行内元素的本质,所以其上下margin依然存在问题

重叠

margin负值并不总是后面元素覆盖前面元素,它与元素display属性有关系

  1. 两个block元素重叠时,后面元素可以覆盖前面元素的背景,但无法覆盖其内容

  2. 当两个inline元素,或两个line-block元素,或inline与inline-block元素重叠时,后面元素可以覆盖前面元素的背景和内容

  3. 当inline元素(或inline-block元素)与block元素重叠时,inline元素(或inline-block元素)覆盖block元素的背景,而内容的话, 后面的元素覆盖前面的元素

在普通流布局中,浏览器将页面布局分为内容和背景,内容的层叠显示始终高于背景。block元素分为内容和背景,而inline元素或inline-block元素,它本身就是内容(包括其背景等样式设置)

浮动

 1. block元素与浮动元素重叠时,其边框和背景在该浮动元素之下显示,而内容在浮动元素之上显示  1. inline或inline-block元素与浮动元素重叠时,其边框、背景和内容都在该浮动元素之上显示

box-sizing 属性

允许开发人员指定盒子宽度和高度的计算方式。

  • 外加模式:此时设置的 width 和 height 是内容区域的宽高。即标准盒模型。

    box-sizing: content-box;
    
  • 内减模式:此时设置的 width 和 height 是包含 border 在内的盒子的总宽高。即 IE 盒模型。

    box-sizing: border-box;
    

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的形式

overflow 属性

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

overflow: visible;
  • visible:默认值。多余的内容不剪切也不添加滚动条,会全部显示出来。
  • hidden:不显示超过对象尺寸的内容。
  • auto:如果内容不超出,则不显示滚动条;如果内容超出,则显示滚动条。
  • scroll:Windows 平台下,无论内容是否超出,总是显示滚动条。Mac 平台下,和 auto 属性相同。

滚动捕捉

在 Web 布局中,时常会碰到内容溢出容器的现状,如果 overflow 设置为 autoscroll 时容器会出现水平或垂直滚动条。

为了给用户提供更好的滚动体验,CSS 提供了一些优化滚动体验的 CSS 特性,其中滚动捕捉就是其中之一。CSS 的滚动捕捉有点类似于 Flexbox 和 Grid 布局的特性,分类为用于滚动容器的属性和滚动项目(定位子项,滚动容器子元素)的属性:

滚动容器 scroll-snap-type
scroll-padding
scroll-snap-stop
滚动项目 scroll-margin
scroll-snap-align

有了滚动捕捉特性,我们不需要依赖任何 JavaScript 库或脚本,就可以实现横向滚动海报时,图片的中心位置和容器中心位置对齐

关键代码就下面这几行:

.container {
  scroll-behavior: smooth;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x mandatory;
  scroll-padding: 20px;
}

img {
  scroll-snap-align: center;
  scroll-snap-stop: always;
}

clip

clip规则指定元素超出容器大小时,具体显示哪个部分。它只对绝对定位(absolute)和固定定位(fixed)的元素有效。top right bottom left分别指最终剪裁可见区域的上边,右边,下边与左边。而所有的数值都表示位置,且是相对于原始元素的左上角而言的。

rect(top right bottom left)
rect(30px 200px 200px 20px)

表示的含义就是:最终剪裁的矩形的上边距离原始元素的上边缘30像素;剪裁矩形的右边缘距离原元素左边缘的距离是200像素;剪裁矩形的下边缘距离原元素顶部的距离为200像素;剪裁矩形的左边缘距离原元素左边缘的距离时20像素。

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

clip:rect矩形剪裁的一些应用介绍

(1) 可用性隐藏

根据上面对top right bottom left的释义,如果left >= right或者bottom <= top,则元素会被完全裁掉而不可见,即“隐藏”。通过这种方式隐藏的元素是可以被屏幕阅读器等辅助设备识别的,从而提高了页面的可用性。

例如:

clip: rect(1px 1px 1px 1px); /*left = right, bottom = top*/

(2)img标签下的CSS Sprite定位

为了节约图片资源,我们经常会把小图片整合到一张图片上,称为图片合并技术,国外称为CSS Sprite,含“精灵”之意。然后利用元素区域外background内容不可见的特性配合background-position定位实现图片的精确显示。

显然,background-position下的CSS Sprite定位也不可能适用于各种情况。

(3)图片剪裁的预览效果

蒙层和剪切

如果你对设计或设计软件较为熟悉的话,对于蒙层和剪切不会感到陌生。设计师在做一些设计稿的时候,时常也会用到蒙层和剪切的能力。随着 CSS 的发展,在 CSS 的世界中也有了这两个特性,它们在 W3C 的 《CSS Masking Module Level 1》规范中定义,主要的作用如下图所示:

蒙层和剪切对应的 CSS 属性就是 maskclip-path

mask 属性

mask 是一个简写属性,它的使用规则和 background 非常的相似。

h1 {
	mask-image: url(mask.png);
}

mask-image 即蒙层图。

而且我们还可以借助 mask 的合成能力,让多个蒙层做合成运算:

clip-path 属性

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

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

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

.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%);
    
}

opacity

CSS 里的 opacity 属性用来设置元素的透明度。透明度会应用到元素内的所有内容,不论是图片,还是文本,或是背景色。

opacity: 0.3;
  • 属性值为 1 代表完全不透明。
  • 属性值为 0.5 代表半透明。
  • 属性值为 0 代表完全透明。

object-fit

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

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

object-position

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

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

outline

outline 属性是在一条声明中设置 outline-style, outline-widthoutline-color轮廓属性的简写属性。

border 和 outline 很类似,但outline不占据空间,绘制于元素内容周围。根据规范,outline通常是矩形,但也可以是非矩形的。

等比缩放

CSS 等比缩放一般指的是 “容器高度按比例根据宽度改变”,很多时候也称为宽高比或纵宽比。 众所周知,我们开发 Web 页面要面对的终端更复杂的了,而这些终端的宽高比都不一样。常见的比例有:

CSS 在还没有 _aspect-ratio_ 之前,常使用一些 Hacck 手段来实现实类似的效果,即使用 padding-toppadding-bottom 来实现:

<aspectratio-container> 
    <aspectratio-content></aspectratio-content> 
</aspectratio-container> 

<style>
    .aspectratio-container { 
        width: 50vmin; /* 用户根据自己需要设置相应的值 */ 

        /* 布局方案可以调整 */ 
        display: flex; 
        justify-content: center; 
        align-items: center; 
    } 

    /* 用来撑开aspectratio-container高度 */ 
    .aspectratio-container::after { 
        content: ""; 
        width: 1px; 
        padding-bottom: 56.25%; 

        /*元素的宽高比*/ 
        margin: -1px; 
        z-index: -1; 
    }
</style>  

有了 CSS 自定义属性之后,可以结合 calc() 函数来实现容器等比缩放的效果:

.container {
    --ratio: 16/9;
    height: calc(var(--width) * 1 / (var(--ratio)));
    width: 100%;
}

虽然比padding-top 这样的Hack 手段简单,但相比原生的aspect-ratio还是要复杂的多。即:

.container {
    width: 100%;
    aspect-ratio: 16 / 9;
}

还可以通过 @media 让元素在不同的终端上按不同的比例进行缩放:

.transition-it {
    aspect-ratio: 1/1;
    transition: aspect-ratio .5s ease;

    @media (orientation: landscape) {
        & {
            aspect-ratio: 16/9;
        }
    }
}

逻辑属性

为了让 Web 开发者能更好的针对不同的阅读模式提供不同的排版效果,在CSS新增逻辑属性。有了逻辑属性之后,以前很多概念都有所变化了。比如我们以前熟悉的坐标轴,x 轴和 y 轴就变成了 inline 轴 和 block 轴,而且这两个轴也会随着书写模式做出调整:

除此之外,我们熟悉的 CSS 盒模型也分物理盒模型和逻辑盒模型:

你可能感知到了,只要是以前带有 toprightbottomleft 方向的物理属性都有了相应的 inline-startinline-endblock-startblock-end 的逻辑属性

内容可见性

CSS 内容可见性,说要是指 content-visibilitcontain-intrinsic-size 两个属性,目前隶属于 W3C 的 CSS Containment Module Level 2 模块,主要功能是可以用来提高页面的渲染性能。

一般来说,大多数Web应用都有复杂的UI元素,而且有的内容会在设备可视区域之外(内容超出了用户浏览器可视区域),比如下图中红色区域就在手机设备屏幕可视区域之外:

在这种场合下,我们可以使用CSS的 content-visibility 来跳过屏幕外的内容渲染。也就是说,如果你有大量的离屏内容(Off-screen Content),这将会大幅减少页面渲染时间。

使用了 CSS 的 content-visibility 属性,浏览器的渲染过程就变得更加容易。本质上,这个属性 改变了一个元素的可见性,并管理其渲染状态。

contain-intrinsic-size 属性控制由 content-visibility 指定的元素的自然尺寸。它的意思是,content-visibility 会将分配给它的元素的高度(height)视为 0,浏览器在渲染之前会将这个元素的高度变为 0,从而使我们的页面高度和滚动变得混乱。但如果已经为元素或其子元素显式设置了高度,那这种行为就会被覆盖。如果你的元素中没显式设置高度,并且因为显式设置 height可能会带来一定的副作用而没设置,那么我们可以使用contain-intrinsic-size来确保元素的正确渲染,同时也保留延迟渲染的好处。

换句话说,contain-intrinsic-sizecontent-visibility 是一般是形影不离的出现:

section {
  content-visibility: auto;
  contain-intrinsic-size: 1000px;
}

内在尺寸

如果你使用浏览器开发者工具审查代码时,将鼠标放到一个 <img> 标签上,你会看到类似下图这样的:

<img> 的 src 路径上浮出来的图片底下有一行对图像的尺寸的描述,即252 x 158 px (intrinsic: 800 x 533 px) ,其实现这表述图片尺寸中两个重要信息:

  • 外在尺寸: 252 x 158 px ,开发者给 img 设置的尺寸
  • 内在尺寸: 800 x 533 px ,图片原始尺寸

其实在 CSS 中给一个元素框设置大小时,有的是根据元素框内在的内容来决定,有的是根据上下文来决定的。根据该特性,CSS的尺寸也分为内部(内在)尺寸和外部(外在)尺寸。

  • 内部尺寸:指的是元素根据自身的内容(包括其后代元素)决定大小,而不需要考虑其上下文;而其中 min-contentmax-contentfit-content 能根据元素内容来决定元素大小,因此它们统称为内部尺寸。
  • 外部尺寸:指的是元素不会考虑自身的内容,而是根据上下文来决定大小。最为典型的案例,就是 widthmin-widthmax-width 等属性使用了 % 单位的值。

通地一个简单的示例来向大家演示 CSS 内在尺寸的特性,即 min-contentmax-contentfit-content 的特性。

<h1>CSS is Awesome</h1>

<style>
  h1 {
    width: auto;
  }
</style>  

  • 从上图中不难发现,width 取值为 min-content 时,h1 的宽度始终是单词“Awesome”长度(大约是144.52px)。它的宽度和容器宽度变化并无任何关系,但它受排版内相关的属性的影响,比如font-sizefont-family 等。
  • h1width 取值为 max-content 时,它的宽度是h1 所在行所有内容的宽度。
  • 简单地说,fit-content 相当于 min-contentmax-content,其 取值:
    • 如果元素的可用空间(Available)充足,fit-content 将使 用 max-content
    • 如果元素的可用空间(Available)不够充足,比 max-content 小点,那就是用可用空间的值,不会导致内容溢出
    • 如果元素的可用空间(Available)很小,比 min-content还小,那就使用 min-content

min-contentmax-contentfit-content 被称之个内在尺寸,它可以运用于设置容器尺寸的属性上,比如widthheight 以及 inline-sizeblock-size 等。但有些属性上使用的话则会无效:

  • min-contentmax-contentfit-content 用于 flex-basis 无效
  • fit-content 用于设置网格轨道尺寸的属性上无效
  • 网格项目或Flex项目上显式设置 width:fit-content也无效,因为它们的默认宽度是 min-content
  • 最好不要在 min-widthmax-width 上使用 fit-content,易造成混乱,建议在 width 上使用 fit-content

格式化上下文

格式化上下文(Formatting Context)是 CSS2.1 规范中的一个概念,规定了渲染区域内部的子元素是如何排版以及相互作用的。

不同类型的盒子有不同格式化上下文:

  • BFC (Block Formatting Context) 块级格式化上下文;
  • IFC (Inline Formatting Context) 行内格式化上下文;
  • FFC (Flex Formatting Context) 弹性格式化上下文;
  • GFC (Grid Formatting Context) 格栅格式化上下文;

BFC

块格式化上下文,它是一个独立的渲染区域,只有块级盒子参与,它规定了内部的块级盒子如何布局,并且与这个区域外部毫不相干。BFC 就好像一个结界,结界里面的东西不能影响外面的布局,也就是说,BFC的子元素再翻江倒海,都不会影响外面的元素。

BFC 渲染规则

  • 内部的盒子会在垂直方向,一个接一个地放置;
  • 盒子垂直方向的距离由 margin 决定,属于同一个 BFC 的两个相邻盒子的 margin 会发生重叠;直接子孙元素与该BFC上下边界margin不能折叠,保证了BFC内部的元素不会影响外部的元素。两个上下相邻的BFC之间折不折叠要看具体情况,如display: inline-blockfloat: left不会折叠,而overflow: hidden则会折叠。
  • 每个元素的 margin 的左边,与包含块 border 的左边相接触(对于从左往右的格式化,否则相反),即使存在浮动也是如此;除非子盒子形成了一个新的BFC。
    • 包含块未必就是父级元素。对于position: absolute来说,包含块是指第一个positoin不为static的祖先元素。
    • BFC中的盒子应该与其自身的包含块相接触,而非与BFC盒子本身相接触。
    • BFC中的盒子是与其包含块的 left edge 相接触,而不是包含块的 left-border 相接触。left edge 正确的翻译为左边缘。左边缘可能是content box的左边缘(非绝对定位如position: relative float: left),也可能是padding box的左边缘(如绝对定位position: absolute position: fixed)。
  • BFC 的区域不会与 float 盒子重叠;
  • BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。它规定了BFC子元素无论margin-top: -10000px float: left 等都不会影响到BFC外部的元素的布局。
  • 计算 BFC 的高度时,浮动元素也参与计算。

创建 BFC

  • 根元素:html
  • 非溢出的可见元素:overflow 值不为 visible 的元素(hidden、auto、scroll)
  • 设置浮动:float 属性不为 none
  • 设置定位:position 为 absolute 或 fixed
  • 定义成块级的非块级元素:display: inline-block/table-cell/table-caption/flex/inline-flex/grid/inline-grid
  • display为flex、grid、flow-root的元素(flow-root为专门设置BFC的属性值)

BFC包含创建该上下文元素的所有子元素,但不包括创建了新BFC的子元素的内部元素。

BFC 应用场景

自适应两栏布局

应用原理:BFC 的区域不会和浮动区域重叠,所以就可以把侧边栏固定宽度且左浮动,而对右侧内容触发 BFC,使得它的宽度自适应该行剩余宽度。

清除内部浮动

浮动造成的问题就是父元素高度坍塌,所以清除浮动需要解决的问题就是让父元素的高度恢复正常。而用 BFC 清除浮动的原理就是:计算 BFC 的高度时,浮动元素也参与计算。只要触发父元素的 BFC 即可。

防止垂直 margin 合并

BFC 渲染原理之一:同一个 BFC 下的垂直 margin 会发生合并。所以如果让 2 个元素不在同一个 BFC 中即可阻止垂直 margin 合并。

IFC

IFC 的形成条件非常简单,块级元素中仅包含内联级别元素,需要注意的是当IFC中有块级元素插入时,会产生两个匿名块将父元素分割开来,产生两个 IFC。

IFC 渲染规则

  • 子元素在水平方向上一个接一个排列,在垂直方向上将以容器顶部开始向下排列;
  • 节点无法声明宽高,其中 margin 和 padding 在水平方向有效在垂直方向无效;
  • 节点在垂直方向上以不同形式对齐;
  • 能把在一行上的框都完全包含进去的一个矩形区域,被称为该行的线盒(line box)。线盒的宽度是由包含块(containing box)和与其中的浮动来决定;
  • IFC 中的 line box 一般左右边贴紧其包含块,但 float 元素会优先排列。
  • IFC 中的 line box 高度由 line-height 计算规则来确定,同个 IFC 下的多个 line box 高度可能会不同;
  • 当内联级盒子的总宽度少于包含它们的 line box 时,其水平渲染规则由 text-align 属性值来决定;
  • 当一个内联盒子超过父元素的宽度时,它会被分割成多盒子,这些盒子分布在多个 line box 中。如果子元素未设置强制换行的情况下,inline box 将不可被分割,将会溢出父元素。

IFC 应用场景

水平居中

当一个块要在环境中水平居中时,设置其为 inline-block 则会在外层产生 IFC,通过 text-align 则可以使其水平居中。

垂直居中

创建一个 IFC,用其中一个元素撑开父元素的高度,然后设置其 vertical-align: middle,其他行内元素则可以在此父元素下垂直居中。

流的破坏

float 属性

float: left;

特性

  • 包裹性:即此时元素width会像height一样由子元素决定,而不是默认撑满父元素。
  • 块状化并格式化上下文:块状是指元素设置float: left之后,其display的计算值就成了block。格式化上下文是指会创建一个BFC。
  • 没有任何margin合并。它向 leftright 浮动,直到它的外边缘碰到包含框或另一个浮动框的边框为止。
  • 脱离文档流:float设计的初衷就是为了“文字环绕”效果,为了让文字环绕图片,就需要具备两个条件。第一是元素高度坍塌,第二是行框盒子不可与浮动元素重叠。而元素高度坍塌就导致元素后面的非浮动块状元素会和其重叠,于是他就像脱离文档流了。

原则:永远不是一个东西单独浮动,浮动都是一起浮动,要浮动,大家都浮动。

clear的作用和不足

clear: both可以清除前面浮动元素的浮动,但实际上,他并不是真的清除了浮动。

clear的定义是:元素盒子的边不能与前面的浮动元素相邻。也就是虽然浮动元素高度坍塌,但是设置了clear: both的元素却将其高度视为仍然占据位置。

clear只能作用于块级元素,并且其并不能解决后面元素可能发生的文字环绕问题。

清除浮动

因为浮动元素会脱离正常的文档流,并不会占据文档流的位置,所以如果一个父元素下面都是浮动元素,那么这个父元素就无法被浮动元素所撑开,这样一来父元素就丢失了高度,这就是所谓的浮动造成的父元素高度坍塌问题。

父元素高度一旦坍塌将对后面的元素布局造成影响,为了解决这个问题,所以需要清除浮动,让父元素恢复高度。

清除浮动方法:

  • 通过给浮动元素的祖先元素加高度。

  • 通过 clear 清除浮动

    • 浮动元素自身拥有clear:both;(针对两个浮动元素之间的影响)

    • 隔墙法:在两个浮动元素之间放置一个有 clear:both; 属性的元素。

    • 内墙法:放置一个有 clear:both; 属性的元素作为浮动元素的兄弟。这种写法的核心原理就是通过 ::after 伪元素为在父元素的最后一个子元素后面生成一个内容为空的块级元素,然后通过 clear 将这个伪元素移动到所有它之前的浮动元素的后面:

      .clearfix:after{
        visibility:hidden;
        display:block;
        font-size:0;
        content:" ";
        clear:both;
        height:0;
      }
      
  • BFC 清除浮动:计算 BFC 高度的时候浮动子元素的高度也将计算在内,利用这条规则就可以清楚浮动。而产生 BFC 的方式很多,我们可以给父元素设置overflow: auto 来简单的实现 BFC 清除浮动,但是为了兼容 IE 最好用 overflow: hidden。通过 overflow: hidden 来清除浮动并不完美,当元素有阴影或存在下拉菜单的时候会被截断,所以该方法使用比较局限。

position 属性

position属性用来指定一个元素在网页上的位置。

static 定位

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

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

注意,static定位所导致的元素位置,是浏览器自主决定的,所以这时topbottomleftright这四个属性无效。

相对定位

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

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

relative表示,相对于默认位置(即static时的位置)进行偏移,即定位基点是元素的默认位置。相对定位占有原有空间,不脱离标准文档流,也不会对其它元素的位置产生影响。

position: relative;
left: 50px;		/* 向右偏移 50px */
top: 50px;		/* 向下偏移 50px */

控制各个方向偏移量的属性是 leftrighttopbottom。 它们代表从原来位置向远离该方向偏移指定的像素、百分比或者 em。 也就是元素将从当前位置向属性相反的方向偏移, 负数表示相同的方向偏移。

绝对定位

position: absolute;
left: 10px;
top/bottom: 20px;

绝对定位的参考点:

  • 如果所有祖先元素都是static定位,定位基点就变成整个网页的根元素html
    • 如果用 top 描述,那么参考点就是页面的左上角,而不是浏览器的左上角。横坐标用left表示,纵坐标用top或者bottom表示。
    • 如果用 bottom 描述,那么参考点就是浏览器首屏窗口尺寸。
  • 一个绝对定位的元素,如果父辈元素中也出现了已定位(非static定位,无论是绝对定位、相对定位,还是固定定位)的元素,那么将以父辈 border 内侧为参考点。即相对于其包含块定位。
  • 定位点在 left、top 时是左上角,在 left、bottom 是左下角,同理其他。

定义

和浮动元素一样,绝对定位也具有块状化、BFC、包裹性、脱离文档流、没有margin合并的特性。

但和浮动不同的是,绝对定位是完全的脱离文档流。大家还记得浮动产生的目的就是为了实现文字环绕效果,所以浮动元素虽然脱离了文档流,但是后面的文字还是会环绕在浮动元素周围。而绝对定位一但产生,就不会再对周围元素产生任何影响。

而且两者包含块不同,浮动元素包含块只能是父级元素,绝对定位的包含块则是距离最近的position不为static的祖先元素。

默认最大宽度为包含块的宽度

默认最大宽度为包含块的宽度。由于这个特性,如果包含块的宽度很小,会出现一柱擎天的现象。解决方法也很简单,加个white-space: nowrap让文本不换行就行了

无依赖绝对定位

无依赖的position: absolute元素定位的位置和其本身无定位属性时候的位置和display的值有关。如果元素在没有position的情况下是内联元素,则和内联元素在同一行显示;如果元素在没有position属性的情况下是块级元素,则换行显示。

绝对定位和 overflow: hidden

overflow: hidden元素在绝对定位元素和其包含块之间的时候,绝对定位元素不会被剪裁。

position: absolute 的流体特性

当绝对定位元素的水平方向(left/right)或垂直方向(top/bottom)的两个定位属性同时存在的时候,绝对元素在该方向上便具有了流体特性。此时的width/height属性具有自动撑满的特性,和一个正常流的div元素的width属性别无二致。

固定定位

position: fixed 是相对于屏幕视口的位置来指定元素位置,祖先元素设置 position: relative 并不会对其产生影响。这会导致元素的位置不随页面滚动而变化,好像固定在网页上一样。类似于绝对位置,它与 CSS 偏移属性一起使用,并且也会将元素从当前的文档流里面移除。

position: fixed;

position: fixed 只有一个要注意的点,那就是当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先。

粘性定位

sticky 能够形成"动态固定"的效果。

生效规则:

  • 须指定 top, right, bottom 或 left 四个阈值其中之一,才可使粘性定位生效,浏览器把它当作sticky的生效门槛。否则其行为与相对定位相同。
    • topbottom 同时设置时,top 生效的优先级高,leftright 同时设置时,left 的优先级高。
  • 设定为 position:sticky 元素的任意父节点的 overflow 属性必须是 visible,否则 position:sticky 不会生效。这里需要解释一下:
    • 如果 position:sticky 元素的任意父节点定位设置为 overflow:hidden,则父容器无法进行滚动,所以 position:sticky 元素也不会有滚动然后固定的情况。
    • 如果 position:sticky 元素的任意父节点定位设置为 position:relative | absolute | fixed,则元素相对父元素进行定位,而不会相对 viewprot 定位。
  • 达到设定的阀值。这个还算好理解,也就是设定了 position:sticky 的元素表现为 relative 还是 fixed 是根据元素是否达到设定了的阈值决定的。

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

#one { position: sticky; top: 10px; }

在 viewport 视口滚动到元素 top 距离小于 10px 之前,元素为相对定位。之后,元素将固定在与顶部距离 10px 的位置,直到 viewport 视口回滚到阈值以上。

z-index 属性

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

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

z-index属性的难点在于,一组设置了z-index属性的元素,它们的堆叠顺序取决于,它们是否处在同一个堆叠上下文(stacking context)。

层叠上下文

在电脑显示屏幕上的显示的页面其实是一个三维的空间,水平方向是 X 轴,竖直方向是 Y 轴,而屏幕到眼睛的方向可以看成是 Z 轴。众 HTML 元素依据自己定义的属性的优先级在 Z 轴上按照一定的顺序排开,而这其实就是层叠上下文所要描述的东西。

创建层叠上下文

  • html 文档根元素

  • 声明 position: absolute/relative 且 z-index 值不为 auto 的元素;

  • 声明 position: fixed/sticky 的元素;

  • flex 容器的子元素,且 z-index 值不为 auto;

  • grid 容器的子元素,且 z-index 值不为 auto;

  • opacity 属性值小于 1 的元素,也就是透明元素;

  • mix-blend-mode 属性值不为 normal 的元素;

  • 以下任意属性值不为 none 的元素:

    • transform
    • filter
    • perspective
    • clip-path
    • mask / mask-image / mask-border
  • isolation 属性值为 isolate 的元素;

  • -webkit-overflow-scrolling 属性值为 touch 的元素;

  • will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素;即值为opacity/transform/filter/isolation/mix-blend-mode中的一个。

  • contain 属性值为 layout、paint 或包含它们其中之一的合成值(比如 contain: strict、contain: content)的元素。

  • 元素的filter值不为none

层叠等级

层叠等级指节点在三维空间 Z 轴上的上下顺序。它分两种情况:

  • 在同一个层叠上下文中,它描述定义的是该层叠上下文中的层叠上下文元素在 Z 轴上的上下顺序;
  • 在其他普通元素中,它描述定义的是这些普通元素在 Z 轴上的上下顺序;

普通节点的层叠等级优先由其所在的层叠上下文决定,层叠等级的比较只有在当前层叠上下文中才有意义,脱离当前层叠上下文的比较就变得无意义了。

层叠顺序

在同一个层叠上下文中如果有多个元素,那么他们之间的层叠顺序:

最底层border/background是当前层叠上下文元素的边框和背景色(注意不包括文字,文字相当于内联盒子)。

普通定位元素层叠水平在普通元素之上。普通定位元素是指z-indexauto的定位元素。

比较两个元素的层叠等级

  • 在同一个层叠上下文中,比较两个元素就是按照上图的介绍的层叠顺序进行比较。
  • 如果不在同一个层叠上下文中的时候,那就需要比较两个元素分别所处的层叠上下文的等级。
  • 如果两个元素都在同一个层叠上下文,且层叠顺序相同,则在 HTML 中定义越后面的层叠等级越高。

渐进增强和优雅降级

渐进增强

一开始就针对低版本浏览器进行构建页面,完成基本的功能,然后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。

.transition { /*渐进增强写法*/
  -webkit-transition: all .5s;
     -moz-transition: all .5s;
       -o-transition: all .5s;
          transition: all .5s;
}

渐进增强的写法,优先考虑老版本浏览器的可用性,最后才考虑新版本的可用性。在前缀CSS3和正常CSS3都可用的情况下,正常CSS3会覆盖前缀CSS3。

优雅降级

一开始就构建站点的完整功能,然后针对浏览器测试和修复。比如一开始使用 CSS3 的特性构建了一个应用,然后逐步针对各大浏览器进行 hack 使其可以在低版本浏览器上正常浏览。

.transition { /*优雅降级写法*/
          transition: all .5s;
       -o-transition: all .5s;
     -moz-transition: all .5s;
  -webkit-transition: all .5s;
}

优雅降级的写法,优先考虑新版本浏览器的可用性,最后才考虑老版本的可用性。在前缀CSS3和正常CSS3都可用的情况下,前缀CSS3会覆盖正常的CSS3。

二者区别

其实渐进增强和优雅降级并非什么新概念,只是旧的概念换了一个新的说法。在传统软件开发中,经常会提到向上兼容和向下兼容的概念。渐进增强相当于向上兼容,而优雅降级相当于向下兼容。向下兼容指的是高版本支持低版本的或者说后期开发的版本支持和兼容早期开发的版本,向上兼容的很少。大多数软件都是向下兼容的比如说Office2010能打开Office2007,Office2006,Office2005,Office2003等建的word文件,但是用Office2003就不能打开用Office2007,Office2010等建的word文件!

优雅降级和渐进增强只是看待同种事物的两种观点。

优雅降级观点认为应该针对那些最高级、最完善的浏览器来设计网站。而将那些被认为“过时”或有功能缺失的浏览器下的测试工作安排在开发周期的最后阶段,并把测试对象限定为主流浏览器(如 IE、Mozilla 等)的前一个版本。在这种设计范例下,旧版的浏览器被认为仅能提供“简陋却无妨 (poor, but passable)” 的浏览体验。你可以做一些小的调整来适应某个特定的浏览器。但由于它们并非我们所关注的焦点,因此除了修复较大的错误之外,其它的差异将被直接忽略。

渐进增强观点则认为应关注于内容本身。请注意其中的差别:我甚至连“浏览器”三个字都没提。内容是我们建立网站的诱因。有的网站展示它,有的则收集它,有的寻求,有的操作,还有的网站甚至会包含以上的种种,但相同点是它们全都涉及到内容。这使得渐进增强成为一种更为合理的设计范例。这也是它立即被 Yahoo! 所采纳并用以构建其“分级式浏览器支持 (Graded Browser Support)”策略的原因所在。

选择器

是元素和其他部分组合起来告诉浏览器哪个HTML元素应当是被选为应用规则中的CSS属性值的方式。选择器所选择的元素,叫做“选择器的对象”。

基本选择器

通用元素选择器

* {
    margin-left: 0px;
    margin-top: 0px;
}

通配符*(星号)匹配任何元素。

因为匹配范围太广,会让浏览器加载页面变慢,因此应该谨慎使用通配符。实际适合使用通配符的情况比较少。

标签选择器

p { font-size:14px; }

匹配所有使用p标签的元素。

类选择器

通过 CSS class 选择器,多个 HTML 元素可以使用相同的 CSS 样式规则。

.info { width:800px; }

<h3 class="info one two"></h3>
  • 匹配所有class属性中包含info的元素。注意在 CSS style 元素里,class 名以一个句点开头。 在 HTML 元素的 class 属性中,class 名的开头没有句点。
  • 任何的标签都可以携带 class 属性。CSS 的 class 具有可重用性,可应用于各种 HTML 元素。同一个标签可以使用多个类选择器,用空格隔开。
  • “类上样式,id 上行为”:class属性交给 css 使用,id属性交给 js 使用。
  • 应该尽量使用 class 选择器,而不是 ID 选择器。ID 选择器的样式不能在其他元素上复用(记住,在一个页面中,一个id只能出现在一个元素上)。这会导致在其他元素上重复样式,而不是通过class共享样式。ID 选择器的特殊性比class选择器要强得多。这意味着如果要覆盖使用id选择器定义的样式,就要编写特殊性更强的CSS规则。如果数量不多,可能还不难管理。如果处理规模较大的网站,其CSS就会变得比实际所需的更长、更复杂。
BEM命名法

BEM是Block(区块)、Element(元素)、Modifier(修饰符)三者的简称。区块是顶级组件的抽象,元素是组件的组成部分,修饰符是组件或元素的状态。区块与元素之间用两个下划线连接,元素与修饰符之间用两个连词线连接。

/* Block component */
.btn {}

/* Element that depends upon the block */
.btn__price {}

/* Modifier that changes the style of the block */
.btn--orange {}
.btn--big {}

对应的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是扁平式的,不存在元素嵌套。

ID 选择器

在声明 id 的时候,也必须在名字前插入 # 符号。

#footer { border:3px dashed green; }

匹配 id 属性等于footer的元素。

根据规范,id 属性应是唯一的。 尽管浏览器并非必须执行这条规范,但这是广泛认可的最佳实践。 因此,请不要给多个元素设置相同的 id

属性选择器

video[autoplay] {}

按照给定的属性,选择所有匹配的元素。

  • E[att]:匹配所有具有att属性的E元素,不考虑它的值。(注意:E在此处可以省略,比如"[cheacked]"。以下同。)
  • E[att="abc"]:匹配所有att属性等于"val"的E元素。
  • E[att~=val]: 匹配所有att属性具有多个空格分隔的值、其中一个值等于"val"的E元素
  • E[att|=val]:匹配所有att属性具有多个连字号分隔(hyphen-separated)的值、其中一个值以"val"开头的E元素,主要用于lang属性,比如"en"、“en-us”、“en-gb"等等
  • E[att^="abc"]:选中页面的E元素,并且E需要带有 att 属性,属性值以 abc 开头。
  • E[att$="abc"]:选中页面的E元素,并且E需要带有 att 属性,属性值以 abc 结尾。
  • E[att*="abc"]:选中页面的E元素,并且E需要带有 att 属性,属性值任意位置包含abc。

属性修饰器支持i修饰符,表示不区分大小写。

[class=foo i] {
  color: red;
}

上面代码中,属性名foo后面的i,表示不区分foo的大小写。

这个修饰符对于匹配用户的输入,非常有用。

/**
 * 匹配:
 * <input value="hello world">
 * <input value="hello World">
 * <input value="hElLo WoRlD">
 * ...
 */
[value="hello world" i] { /* ... */ }

分组选择器

交集选择器

h3.special {/* 选中 h3 标签且有 special 类 */
    color: red;
}

定义交集选择器的时候,两个选择器之间紧密相连。

多元素选择器

p, #id, .one {/* 选中 p 或id为 id 或有 one 类 */
    color: red;
}

同时匹配所有E元素或F元素即并集,E和F之间用逗号分隔。

:matches()

:matches(A, B)选择器表示匹配A或B。

:matches(.foo, .bar) {
  background-color: green;
}

/* 等同于 */

.foo, .bar {
  background-color: green;
}

它可以简化一些选择器的写法。

.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);
}

组合器

后代元素选择器

.div1 p {/* 选中类 .div1 后代标签 p */
	color: red;
}

匹配所有属于E元素后代的F元素,E和F之间用空格分隔

子元素选择器

div>p {}

匹配所有E元素的子元素F

一般兄弟组合器

div~p {}		/* 选中的div后面所有的p	*/

匹配任何在E元素之后的同级F元素。~ 组合器选择兄弟元素,也就是说,后一个节点在前一个节点后面的任意位置,并且共享同一个父节点。

毗邻元素选择器

div+p {}		/* 选中div后面相邻的第一个p */

+ 组合器选择相邻元素,即后一个元素紧跟在前一个之后,并且共享同一个父节点。

伪选择器

伪类选择器

a:link{
    color:red;
}

a:visited{
    color:orange;
}

a:hover{
    color:green;
}

a:active{
    color:black;
}

伪类选择标签的不同状态,伪类用冒号来表示。

条件伪类

  • :lang():基于元素语言来匹配页面元素;匹配lang属性等于c的E元素

  • :dir():匹配特定文字书写方向的元素;

  • :has():匹配包含指定元素的元素;它可以用来选择父级元素。其中 :not(:has(selector)) 匹配不含有 selector 元素的父元素,而 :has(:not(selector)) 匹配含有的不是 selector 子元素的元素。两者主要区别在于,:has(:not(selector)) 写法必须要含有一个子元素,而 :not(:has()) 可以不含有元素也会被匹配。

  • :is()where():匹配指定选择器列表里的元素;

    :is(.header, .main) .p { /**020**/
    	color: purple
    }
    
    :where(.header, .main) .p {	/**010**/
    	color: red;
    }
    

    等同于

    .header .p,
    .main .p {
    	color: purple;
    }
    

    他们唯一不同之处,就是选择器权重不同。:where() 的优先级总是为 0 ,但是 :is() 的优先级是由它的选择器列表中优先级最高的选择器决定的。

    :is():where() 伪类选择器的出现,将会让我们的选择器变得更简洁

    h1 {
      font-size: 30px;
    }
    
    section h1, article h1, aside h1, nav h1 {
      font-size: 25px;
    }
    
    section section h1, section article h1, section aside h1, section nav h1,
    article section h1, article article h1, article aside h1, article nav h1,
    aside section h1, aside article h1, aside aside h1, aside nav h1,
    nav section h1, nav article h1, nav aside h1, nav nav h1, {
      font-size: 20px;
    }
    

    使用 :is() 可以像下面这样来描述:

    h1 {
      font-size: 30px;
    }
    
    :is(section, article, aside, nav) h1 {
      font-size: 25px;
    }
    
    :is(section, article, aside, nav)
    :is(section, article, aside, nav) h1 {
      font-size: 20px;
    }
    
  • :not():匹配不符合当前选择器的任何元素。:not()可以采用链式写法。

    :not(i):not(em)
    

    等同于

    :not(i, em)
    

    希望最后一张卡片没有margin-bottom (或第一张卡片没有 margin-top)。针对于这样的场景,:not() 选择器就非常有优势。

    .card:not(:last-child) {
    	margin-bottom: 20px
    }
    

行为伪类

针对所有标签都适用的样式。

  • :hover:鼠标放到标签上时。
  • :active: 鼠标点击标签,但是不松手时

状态伪类

只能用于超链接的样式。

  • :link:超链接点击之前。a{}a:link{} 的区别:

    • a{}:定义的样式针对所有的超链接
    • a:link{} :定义的样式针对所有写了 href 属性的超链接
  • :visited:链接被访问过之后。

  • :target:匹配文档中特定"id"点击后的效果。

  • :focus:当用户使用鼠标点击焦点元素或使用键盘的 Tab 键(或快捷键)触发焦点元素焦点环的样式。如果我们要让 :focus:focus-visible 可以有独自的样式,可以借助CSS选择器中的 :not() 来处理:

    button:focus:not(:focus-visible) {
      outline: 2px dotted #416dea;
      outline-offset: 2px;
      box-shadow: 0px 1px 1px #416dea;
    }
    
    button:focus-visible {
      outline: 2px solid #416dea;
      outline-offset: 2px;
      box-shadow: 0px 1px 1px #416dea;
    }
    
    
  • :required:输入必填的表单元素;

  • :valid:输入合法的表单元素;

  • :invalid:输入非法的表单元素;

  • :in-range:输入范围以内的表单元素;

  • :out-of-range:输入范围以外的表单元素;

  • :checked:选项选中的表单元素;匹配表单中被选中的radio(单选框)或checkbox(复选框)元素。

  • :optional:选项可选的表单元素;

  • :enabled:事件启用的表单元素;匹配表单中激活的元素。

  • :disabled:事件禁用的表单元素;匹配表单中禁用的元素。

  • :read-only:只读的表单元素;

  • :read-write:可读可写的表单元素;

  • :blank:blank 要比 :empty 灵活地多,只要该元素中无任何子元素都能被识别。不过 W3C 规范对该伪类选择器的定义更趋向于作用到表单控件中,比如用户没有在 inputtextarea 中输入内容时,提交表单能被识别到。有点类似于表单验证的功能。

  • :current():浏览中的元素;

  • :past():已浏览的元素;

  • :future():未浏览的元素;

  • :focus-visible :只有使用键盘的 Tab 键(或快捷键)触发焦点元素焦点环的样式。如果仅使用 :focus-visible 设置焦点环样式的话,那么用户使用鼠标点击焦点元素时不会触发焦点环样式

  • :focus-within:表示一个元素获得焦点,或该元素的后代元素获得焦点。这也意味着,它或它的后代获得焦点,都可以触发 :focus-within。更简单的说,它有点类似于 JavaScript 的事件冒泡,从可获得焦点元素开始一直冒泡到HTML的根元素 <html> ,都可以接收触发 :focus-within 事件。

a:link 、a:visited 、a:hover 、a:active 这四种状态必须按照固定的顺序写。在写 a:linka:visited 这两个伪类的时候,要么同时写,要么同时不写。

结构伪类选择器

  • E:first-child:匹配 E 的父元素的第一个子元素E。
  • E:last-child:匹配 E 的父元素的最后一个子元素E。等同于:nth-last-child(1)。
  • E:nth-child(n):匹配 E 的父元素的第n个子元素E。:nth-child(n),n从1开始计数。:nth-child(an+b),n从0开始计数。
  • E:nth-child(odd):匹配 E 的父元素奇数子元素。
  • E:nth-child(even):匹配 E 的父元素偶数子元素。
  • E:nth-last-child(n):匹配 E 的父元素的倒数第n个子元素E。编号是从1开始算起。
  • :only-child:父元素仅有该元素的元素;等同于:first-child:last-child或 :nth-child(1):nth-last-child(1)。
  • E:first-of-type:匹配同类型中的第一个同级兄弟元素E。等同于:nth-of-type(1)。
  • E:last-of-type:匹配同类型中的最后一个同级兄弟元素E。等同于:nth-last-of-type(1)。
  • E:nth-of-type(n):匹配同类型中的第n个同级兄弟元素E。
  • E:nth-last-of-type(n):匹配同类型中的倒数第n个同级兄弟元素E。
  • :only-of-type:父元素仅有该标签的标签;等同于:first-of-type:last-of-type或 :nth-of-type(1):nth-last-of-type(1)。
  • :root:匹配文档的根元素,对于HTML文档,就是HTML元素。
  • E:empty:empty 只能选中没有子元素的元素。子元素只可以是元素节点或文本(包括空格)。注释或处理指令都不会产生影响。注意,在空元素上即使使用伪元素 ::before::after 创建内容,也能被:empty 识别。
  • E:target:匹配相关URL指向的E元素。要配合锚点使用。

伪元素选择器

p::before { content: "Hello world!"; } 
  • E::before:在E元素之前插入生成的伪元素,其将成为匹配选中的元素的第一个子元素。此元素默认为行内元素。
  • E::after:在E元素之后插入生成的伪元素,作为已选中元素的最后一个子元素。这个虚拟元素默认是行内元素。
  • E::first-letter:设置元素 E 里面的第一个字符的样式。选中首个字符。
  • E::first-line:设置元素 E 里面的第一行的样式。
  • E::selection:设置元素 E 里面被鼠标选中的区域的样式。

::before::after 必须配合 content 来使用。 这个属性通常用来给元素添加内容诸如图片或者文字。 尽管有时 ::before::after 是用来实现形状而非文字,但 content 属性仍然是必需的,此时它的值可以是空字符串,没有 content 属性元素就不会显示出来。

继承性和层叠性

继承性

在css中,每个CSS 属性定义的概述都指出了这个属性是默认继承的(“Inherited: Yes”) 还是默认不继承的(“Inherited: No”)。这决定了当你没有为元素的属性指定值时该如何计算值。

—— MDN

初始值是指当属性没有指定值时的默认值,如这些语句的值都是默认值:background-color: transparentleft: autofloat: nonewidth: auto 等。部分属性的默认值是会根据元素的 display 属性的值而计算的,比如 vertical-align 属性,如果是 display: inline 元素,则其计算值为基线对齐 vertical-align: baseline ,如果是 display: inline-block 元素,则其计算值为 vertical-align: bottom

css 的继承很简单,分为默认继承的和默认不继承的,但所有属性都可以通过设置 inherit 实现继承。

当没有指定值时,默认继承的属性取父元素的同属性的计算值(相当于设置了 inherit ),默认不继承的属性取属性的初始值(相当于设置了 initial )。

默认继承的 (“Inherited: Yes”) 的属性

  • 所有元素默认继承:visibility、cursor
  • 文本属性默认继承:letter-spacing、word-spacing、white-space、line-height、color、font、 font-family、font-size、font-style、font-variant、font-weight、text-indent、text-align、text-shadow、text-transform、direction
  • 列表元素默认继承:list-style、list-style-type、list-style-position、list-style-image
  • 表格元素默认继承:border-collapse

四个通用属性值

属性值分为三种,即 css 规范定义的初始值、浏览器厂商重置的用户代理样式和我们开发人员设置的样式,优先级也是按照这个顺序递增。

css 为控制继承提供了四个特殊的通用属性值,每个 css 属性都能使用这些值:

inherit

设置该属性会使子元素属性和父元素相同。实际上,就是“开启继承”。

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

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

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

.sub-list {
  border: inherit;
}

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

initial

将属性的初始值应用于元素。实际上,就是重置为css规范定义中的默认值(不是浏览器定义的样式表中的样式)。它的主要用处是,让那些默认继承父元素的 CSS 属性不再继承,回到初始值。

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

.berries h1 {
  color: initial;
}

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

unset

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

h1 {
  color: blue;
}

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

.berries h1 {
  color: unset;
}

上面代码中,divh1的父元素,如果不设置.berries h1color属性,h1会显示为蓝色,设成color: unset以后,h1继承了父元素的color,显示为红色。如果不设置divcolor,那么.berries h1将显示为浏览器赋予的默认颜色(黑色)。

revert

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

  • revert用在网站提供的样式表:则显示用户样式表设置的值。如果不存在用户样式表,则浏览器赋予的默认值。
  • revert用在用户提供的样式表:显示浏览器赋予的默认值。

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

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

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

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

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

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

层叠性

用于合并来自多个源的属性值的算法。比如说针对某个 HTML 标签,有许多的 CSS 声明都能作用到的时候,那最后谁应该起作用呢?理解层叠性的时候需要结合 CSS 选择器的优先级以及继承性来理解。(相同的属性,优先级高的覆盖优先级低的,不同属性会合并)

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

  • 特殊性(specificity)
  • 顺序(order)
  • 重要性(importance)

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

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

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

权重

权重值 选择器
1,0,0,0,0 !important
0,1,0,0,0 内联样式:style=”"
0,0,1,0,0 ID选择器:#idName{...}
0,0,0,1,0 类、伪类、属性选择器:.className{...} / :hover{...} / [type="text"] ={...}
0,0,0,0,1 标签、伪元素选择器:div{...} / :after{...}
0,0,0,0,0 通用选择器(*)、子选择器(>)、相邻选择器(+)、同胞选择器(~)
  • 有直接选中了的就不考虑没选中的,这时没选中的权重计算为 0。只有都没选中才考虑没选中。
  • 并集选择器拆开计算。
  • CSS权重进制在IE6为256,后来扩大到了65536,而现代浏览器则采用更大的数量。所以我们可以忽略进制的问题,从高往低比较权重值。
  • 先从高等级进行比较,高等级相同时,再比较低等级的,以此类推;
  • 完全相同的话,就采用后者优先原则;

!important

k:v !important;
  • !important 提升的是一个属性权重,而不是一个选择器。例如 font-family: "Trebuchet MS" , Verdana, Helvetica, Sans-Serif !important; 会覆盖 font-family 属性为 "Trebuchet MS" , Verdana, Helvetica, Sans-Serif
  • !important 在没选中的前提下,无法提升继承的权重比选中的权重还高。
  • !important 在都没选中的前提下,不影响就近原则。
  • 宽高有例外情况,由于宽高会被max-width/min-width覆盖,所以!important会失效。
  • 很多时候,你会使用 CSS 库, CSS 库中的样式会意外覆盖你的 CSS 样式。 如果想保证你的 CSS 样式不受影响,你可以使用 !important

Web 字体

字体族

  • serif:衬线体。
  • sans-serif:无衬线体。
  • monospace:等宽字体。每一个字母所占的宽度是相同的。写代码的字体尽量用等宽字体。
  • cursive:手写字体。比如徐静蕾手写体。
  • fantasy:梦幻字体。比如一些艺术字。

字体格式

  • 内嵌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。

SVG 图像

SVG 是一种基于 XML 语法的图像格式,全称是可缩放矢量图(Scalable Vector Graphics)。其他图像格式都是基于像素处理的,SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。

  • SVG 文件可以直接插入网页,成为 DOM 的一部分,然后用 JavaScript 和 CSS 进行操作。

    <svg
      id="mysvg"
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 800 600"
      preserveAspectRatio="xMidYMid meet"
    >
      <circle id="mycircle" cx="400" cy="300" r="50" />
    <svg>
    
  • SVG 代码也可以写在一个独立文件中,然后用<img><object><embed><iframe>等标签插入网页。

    <img src="circle.svg">
    <object id="object" data="circle.svg" type="image/svg+xml"></object>
    <embed id="embed" src="icon.svg" type="image/svg+xml">
    <iframe id="iframe" src="icon.svg"></iframe>
    
  • CSS 也可以使用 SVG 文件。

    .logo {
        background: url(icon.svg);
    }
    
  • SVG 文件还可以转为 BASE64 编码,然后作为 Data URI 写入网页。

    <img src="data:image/svg+xml;base64,[data]">
    

字体图标

把网页中一些小的图标,借助工具生成一个字体包,然后就可以像使用文字一样使用图标了。

免费可商用字体

  • 思源黑体
  • 思源宋体
  • 文泉驿系列字体
  • Arual

字体属性

font-weight

font-weight 属性用于设置文本中字体的粗细。

font-weight: normal;
font-weight: bold; 

font-weight: 400;		/* = normal */
font-weight: 900;		/* = blod */

所有数值关键字浏览器都支持,有时候之所以没有看到粗细的变化,是因为所使用的字体不支持。如“微软雅黑”,它只支持 400 和 700 这两种粗细。

font-size

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

font-size: 14px;

font-smoothing

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

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

font-display

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

@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

line-height

line-height 属性来设置行间的距离。 行高,顾名思义,可以用来设置每行文字所占据的垂直空间。

line-height: 1.6;		/* 1.6 * font-size */

桌面浏览器默认值为 normal

字母 x 的角色

在内联元素的垂直方向对齐中,基线是最为重要的概念。line-height的大小定义(等于)的就是两基线之间的距离,vertical-align的默认值就是基线。基线的定义则是字母 x 的下边缘。

css中有个概念叫x-height,指的是小写字母 x 的高度。vertical-align: middle对齐的就是基线往上1/2x-height高度的地方,可以理解为近似字母 x 的交叉点。

css中除了px/em/rem等,还有个单位是ex。指的就是小写字母x的高度,即x-height

属性值

  • normal: 默认值normal其实是类型为数值的变量,根据浏览器和字体 font-family 不同而不同,一般约为 1.2 。
  • 数值和百分比:最终会被计算为带单位的值,具体计算方法就是乘以字体font-size大小。
  • 长度值:就是100px这样带单位的值。
  • 这几类值的继承特性不同:line-height是数值的元素的子元素继承的就是这个数值,百分比/长度值继承的都是计算后得出的带单位的值(px)。

文本垂直居中原理

line-height属性用于设置多行元素的空间量,如多行文本的间距。

对块级元素来说,line-height决定了行框盒子的最小高度。注意是行框盒子的最小高度,而不是块级元素的实际高度。

对于非替代的 inline 元素,它用于计算行框盒子的高度。此时内联元素的行框盒子的高度完全由line-height决定,不受其他任何属性的影响。

如果我们把一段文本的line-height设置为父容器的高度就可以实现文本垂直居中了。line-height实现垂直居中的本质——行距。行距是指一行文本和相邻文本之间的距离。上下行距 = line-heightfont-size。行距具有上下等分的机制:意思就是文字上下的行距是一样的,各占一半,这也是line-height能让内联元素垂直居中的原因。如果没有文字而有内联元素,会产生幽灵空白节点,而幽灵空白节点+行高也能撑起div

内联元素的大值特性:无论内联元素的line-height如何设置,最终父元素的高度都是数值大的那个line-height决定的。

vertical-align

vertical-align: middle;

属性值

  • 线类: 如baseline(默认值) top middle bottombaseline使元素的基线与父元素的基线对齐,middle使元素的中部与父元素的基线往上x-height的一半对齐。top bottom使元素及其后代元素的底部与整行或整块的底部对齐。)
  • 文本类: text-top text-bottom(使元素的顶部与父元素的字体顶部对齐。)
  • 上标下标: sub super(使元素的基线与父元素的下标基线对齐。)
  • 数值: 20px 2em (默认值baseline相当于数值的 0 。使元素的基线对齐到父元素的基线之上的给定长度,数值正值是基线往上偏移,负值是往下偏移,借此可以实现元素垂直方向精确对齐。)
  • 百分比: 20% (使元素的基线对齐到父元素的基线之上的给定百分比,该百分比是line-height属性的百分比。)

作用前提

vertical-align属性起作用的前提必须是作用在内联元素上。即display计算值为inline inline-block inline-table table-cell的元素。

display: inline-block 基线的不同之处

一个设置了display: inline-block的元素:

  • 如果元素内部没有内联元素,则该元素基线就是该元素下边缘。
  • 如果元素设置了overflowhidden auto scroll,则其基线就是该元素下边缘。
  • 如果元素内部还有内联元素,则其基线就是内部最后一行内联元素的基线。

font-family

通过 font-family 属性,我们可以设置元素里的字体族名。

font-family: "Times New Roman", "Microsoft YaHei", monospace;

简写:

font: [font-weight] font-size/[line-height] font-family;

多字体 fallback 机制:当指定的字体找不到(或者某些文字不支持这个字体)时,那就接着往后找。monospaceserifsans-serif 等通用字体名不区分大小写,同时,也不需要使用引号,因为它们是 CSS 中的关键字。

在我们的网站上,除了使用系统提供的默认字体以外,我们也可以使用自定义字体。 网络上有很多字体库。 如Google 字体库

字体名区分大小写,并且如果字体名含有空格,则在声明时需要用引号包起来。 例如,使用 "Open Sans" 字体需要添加引号,而 Lobster 则不需要。

文本属性

空格

基本规则

HTML 代码的空格通常会被浏览器忽略。

<p>◡◡hello◡◡world◡◡</p>

上面是一行 HTML 代码,文字的前部、内部和后部各有两个空格。为了便于识别,这里使用半圆形符号表示空格。

浏览器的输出结果如下。

hello world

可以看到,文字的前部和后部的空格都会忽略,内部的连续空格只会算作一个。这就是浏览器处理空格的基本规则。

如果希望空格原样输出,可以使用<pre>标签。

<pre>◡◡hello◡◡world◡◡</pre>

另一种方法是,改用 HTML 实体&nbsp;表示空格。

<p>&nbsp;&nbsp;hello&nbsp;&nbsp;world&nbsp;&nbsp;</p>

空格字符

HTML 处理空格的规则,适用于多种字符。除了普通的空格键,还包括制表符(\t)和换行符(\r\n)。

浏览器会自动把这些符号转成普通的空格键。

<p>hello
world</p>

上面代码中,文本内部包含了一个换行符,浏览器视同为空格,输出结果如下。

hello world

所以,文本内部的换行是无效的(除非文本放在<pre>标签内)。

<p>hello<br>world</p>

上面代码使用<br>标签显式表示换行。

white-space

HTML 语言的空格处理,基本上就是直接过滤。这样的处理过于粗糙,完全忽视了原始文本内部的空格可能是有意义的。

CSS 提供了一个 white-space 属性,可以提供更精确一点的空格处理方式。该属性共有六个值,除了一个通用的inherit(继承父元素),下面依次介绍剩下的五个值。

white-space: normal

white-space属性的默认值为normal,表示浏览器以正常方式处理空格。

<p>◡◡hellohellohello◡hello
world
</p>

上面代码中,文本前部有两个空格,内部有一个长单词和一个换行符。

然后,容器<p>指定一个比较小的宽度。为了便于识别,背景色设为红色。

p {
  width: 100px;
  background: red;
}

显示效果如下图。

可以看到,文首的空格被忽略。由于容器太窄,第一个单词溢出容器,然后在后面一个空格处换行。文本内部的换行符自动转成了空格。

white-space: nowrap

white-space属性为nowrap时,不会因为超出容器宽度而发生换行。

p {
  white-space: nowrap;
}

显示效果如下图。

所有文本显示为一行,不会换行。

white-space: pre

white-space属性为pre时,就按照<pre>标签的方式处理。

p {
  white-space: pre;
}

显示效果如下图。

上面结果与原始文本完全一致,所有空格和换行符都保留了。

white-space: pre-wrap

white-space属性为pre-wrap时,基本还是按照<pre>标签的方式处理,唯一区别是超出容器宽度时,会发生换行。

p {
  white-space: pre-wrap;
}

显示效果如下图。

文首的空格、内部的空格和换行符都保留了,超出容器的地方发生了折行。

white-space: pre-line

white-space属性为pre-line时,意为保留换行符。除了换行符会原样输出,其他都与white-space:normal规则一致。

p {
  white-space: pre-line;
}

显示效果如下图。

除了文本内部的换行符没有转成空格,其他都与normal的处理规则一致。这对于诗歌类型的文本很有用。

direction

direction属性设置块级元素内部的文本阅读方向。

它的默认值是ltr(从左到右),适用于大部分语言。它还可以设成rtl(从右到左)。

blockquote {
  direction: rtl;
}

一般采用 HTML 语言的dir属性控制文本阅读方向,而不是使用这个属性。

text-align

text-align属性设置块级元素内部的文本对齐方式。

text-align: center;

它可以取以下值。

  • 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:继承父元素的值

这个属性通常用于同一行的图标与文字的对齐。

vertical-align: middle;

这个命令对设为display: table-cell的元素也有效,可以控制元素在单元格之中的垂直对齐方式。这时,一般使用topmiddlebottom等值。

Tab-size

tab-size属性设置 Tab 键的宽度,可以设置为整数(表示多少个空格),也可以设置为具体的长度单位。

/* 整数植 */
tab-size: 4;
tab-size: 0;

/* 长度单位 */
tab-size: 10px;
tab-size: 2em;

该属性常用于<pre>标签之中。

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 lang="en">

CSS 里面使用自动连词,要开启hyphens属性。hyphens属性控制块级元素之中,文本是否显示连词线。

hyphens: auto;

hyphens属性可以取以下三个值。

none

none表示一个词不会在断行处被拆开,即断行处不会有连词线。

manual

manual表示只有在一个词内部的字符表示可以有连词线时,才会在断行处拆开。断行处,会有连词线。

两个字符可以表示此处可以断行,并显示连词线,一个是-(U+2010),表示此处可以有一个可见的断行,即使不在此处断行,这里也会有连词线显示;另一个是U+00AD,表示不可见的断行,HTML 文档里面可以用&shy;插入。

auto

auto表示浏览器决定一个词是否可以在断行处拆开,以及是否会有连词线。

letter-spacing

单个字母之间的间距

letter-spacing: 1px ;
  • normal:此间距是按照当前字体的正常间距确定的。和 0 不同的是,用户代理根据此属性来确定文字的默认对齐方式。
  • <length>:指定文字间的间距以替代默认间距。可以是负值,但有可能会出现 implementation 限制。用户代理不会在此基础上进一步增加或缩减间距来对齐文字。

word-spacing

word-spacing: 1px;

word-spacing指的是字符“空格”的间隙。如果一段文字中没有空格,则该属性无效。如中文文字下用word-spacing 没有效果的。

  • normal:正常的单词间距,由当前字体和/或浏览器定义。
  • <length>:通过指定具体的额外间距来增加字体的单词间距。
  • <percentage>:通过指定受影响字符的宽度的百分比的方式来增加的间距。

text-decoration

文本可以附加装饰线(比如下划线),以下属性用来设置装饰线。

text-decoration: none;
  • none:没有任何修饰
  • underline:下划线,默认线宽1px
  • overline:上划线,默认线宽1px
  • line-through:删除线,默认线宽1px
  • inherit:继承父元素的设置

多种装饰线可以同时存在。

.multiple {
  text-decoration: underline overline line-through;
}

默认情况下,装饰线的颜色与文本的color属性一致。text-decoration-color属性可以修改颜色。

text-decoration属性还可以用作text-decoration-styletext-decoration-color的简写形式。

.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设置文本的装饰线的颜色。

a {
  text-decoration-color: #E18728;
}

text-decoration-style

text-decoration-style设置文本的下划线(underline)、上划线(overline)和删除线(line-through)的样式。

a {
  text-decoration-style: solid;
}

该属性支持以下样式。

  • solid:单条直线
  • double:双条直线
  • dotted:多个点组成的直线
  • dashed:多个短划线组成的直线
  • wavy:波浪线

text-decoration-line

text-decoration-line设置文本采用何种装饰线,与text-decoration单个值的写法相同。建议采用后者,因为浏览器的支持度更好。

p {
  text-decoration-line: underline;
}

它的取值参考text-decoration

text-decoration-skip

text-decoration-skip设置文本的装饰线应该在哪里中断,主要用于改善文本被装饰以后的可读性。

a {
  text-decoration-skip: ink;
}

上面代码中,下划线遇到英语字母yp会中断,让它们较长的下划会更清晰地显示出来。

该属性可以取以下值。

  • objects:默认值,装饰线遇到图片或其他inline-block对象时中断。
  • none:装饰线不会有任何中断,包括遇到行内对象。
  • spaces:装饰线在空格、断词处中断。
  • ink:装饰线遇到有笔画下降或上升的字母时中断。
  • edges:装饰线在内容开始后和结束前都收缩一点,主要用于多个连续的装饰线,可以看上去连成一条。
  • box-decoration:装饰线在继承的 margin、border 和 padding 处中断。

color

字体颜色

color:red;

text-transform

text-transform 属性可以改变英文字母的大小写。 使用这个属性时,我们无需改变 HTML 元素中的文本也可以统一页面里英文的显示。

text-transform: lowercase;
结果
lowercase “transform me”
uppercase “TRANSFORM ME”
capitalize “Transform Me”
initial 使用默认值
inherit 使用父元素的 text-transform 值。
none Default:不改变文字。

应用

假设有个输入框只能输入大写字母,那么如下设置,输入小写字母出现的却是大写字母,可用于身份证输入框或验证码输入框等:

input {
    text-transform: uppercase;
}

text-shadow

设置文本的阴影。

text-shadow: 20px 27px 22px pink;
/* 水平位移 垂直位移 模糊程度 阴影颜色 */

text-indent

text-indent 属性能定义一个块元素首行文本内容之前的缩进量。

/* <length> 长度值 */
text-indent: 3mm;
text-indent: 40px;

/* <percentage>百分比值取决于其包含块(containing block)的宽度*/
text-indent: 15%;

/* 关键字 */
text-indent: 5em each-line;
text-indent: 5em hanging;
text-indent: 5em hanging each-line;

/* 全局值 */
text-indent: inherit;
text-indent: initial;
text-indent: unset;
  • each-line:文本缩进会影响第一行,以及使用<br>强制断行后的第一行。
  • hanging:该值会对所有的行进行反转缩进:除了第一行之外的所有的行都会被缩进,看起来就像第一行设置了一个负的缩进值。

text-overflow

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

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

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

initial-letter

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

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

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

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

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

user-select

user-select 属性控制用户能否选中文本。值为 none,元素及其子元素的文本不可选中。

cursor

cursor: pointer;
  • auto:默认值。浏览器根据当前情况自动确定鼠标光标类型。
  • pointer:竖起食指的手形光标。
  • hand:竖起食指的手形光标。
  • all-scroll:上下左右四个箭头,中间有一个圆点的光标。用于标示页面可以向上下左右任何方向滚动。
  • col-resize:左右两个箭头,中间由竖线分隔开的光标。用于标示项目或标题栏可以被水平改变尺寸。
  • crosshair:简单的十字线光标。
  • default:客户端平台的默认光标。通常是一个箭头。
  • move :十字箭头光标。用于标示对象可被移动。
  • help: 带有问号标记的箭头。用于标示有帮助信息存在。
  • no-drop:带有一个被斜线贯穿的圆圈的手形光标。用于标示被拖起的对象不允许在光标的当前位置被放下。
  • not-allowed:禁止标记(一个被斜线贯穿的圆圈)光标。用于标示请求的操作不允许被执行。
  • progress:带有沙漏标记的箭头光标。用于标示一个进程正在后台运行。
  • row-resize:IE6.0 有上下两个箭头,中间由横线分隔开的光标。用于标示项目或标题栏可以被垂直改变尺寸。
  • text : 用于标示可编辑的水平文本的光标。通常是大写字母 I 的形状。
  • vertical-text :用于标示可编辑的垂直文本的光标。通常是大写字母 I 旋转90度的形状。
  • wait:用于标示程序忙用户需要等待的光标。通常是沙漏或手表的形状。
  • *-resize:用于标示对象可被改变尺寸方向的箭头光标。(w-resize | s-resize | n-resize | e-resize | ne-resize | sw-resize | se-resize | nw-resize)
  • url ( url ) :用户自定义光标。使用绝对或相对 url 地址指定光标文件。

pointer-events

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

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

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

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

列表属性

list-style-image

list-style-image: url(images.gif);

list-style-type

CSS 属性 list-style-type 可以设置列表元素的 marker(比如圆点、符号、或者自定义计数器样式)。

list-style-type: none

不显示列表项的标记。

list-style-position

list-style-position属性指示如何相对于对象的内容绘制列表项标记。

img

背景属性

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

opaque

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

background-color

background-color: red;
background-color: rgb(255,0,0);
background-color: #ff0000;
background-color: rgba(0, 0, 255, 0.3);
background-color: hsla(240,50%,50%,0.4);

background-image

将图像设置为背景。 图片链接的地址应写在括号内,一般会用引号包起来。

background-image:url(image.gif);

Gradient

前置知识

  1. 绘制区域,也就是规范中所谓的 gradient box(为了理解无歧义,下文不再翻译该术语),跟所关联 DOM 的实际尺寸有关,比如,设定 background 的话,绘制区域就是 DOM 的 padding box,除非用 backgroud-size 指定大小;如果是设定 list-style-image,绘制区域就是 1em 的正方形。

  2. W3C 的描述中可以知道,浏览器实际是根据 Gradient 指定的语法来生成对应的图片

    A gradient is an image that smoothly fades from one color to another. 
    

    而且不只 background 可以用,比如

    background: linear-gradient(white, gray);
    list-style-image: radial-gradient(circle, #006, #00a 90%, #0000af 100%, white 100%);
    
  3. 由于是 image,所以用于 background 时,实际是设置在 background-image 上

线性渐变(linear-gradient)

background-image: linear-gradient(方向, 起始颜色, 终止颜色);
background: linear-gradient(90deg, red, yellow, rgb(204, 204, 255));

语法如下:

linear-gradient() = linear-gradient(
  [ | to ]?,
  <color-stop-list>
)
<side-or-corner> = [left | right] || [top | bottom]

第一个参数指明了颜色渐变的方向:

  • 可以是角度,比如 0deg,表示正上方向,90deg 表示向右(顺时针方向 90°)
  • 也可以是关键词,比如 to top, to right, to bottom, to left 分别对应了 0deg, 90deg, 180deg, 270deg。当然也可以不指定,默认值是 to bottom

第二个参数指明了颜色断点(即 color-stop)。位置可以省略,第一个默认为 0%,最后一个默认为 100%,如果中间的值没有指定,则按颜色数目求均值,比如

linear-gradient(red 40%, white, black, blue)
/*等价于*/
linear-gradient(red 40%, white 60%, black 80%, blue 100%)

浏览器是如何绘制渐变线的呢?

如下图,从 gradient box 的中心(对角线交点)开始以 CSS 中指定的角度向两侧延伸,终点是 gradient box 的一个相近顶点到 gradient line 垂线的垂足,起点也是类似的求法,两点间的距离就是 gradient line 的长度(浓浓的初中几何味~)。

所以,gradient line 的长度计算公式是:

abs(W * sin(A)) + abs(H * cos(A))
A是角度,W是gradient box的宽,H为高
  • 栗子一:以下的写法效果其实都一样
  background-image: linear-gradient(yellow, green); // 默认方向为to bottom
  background-image: linear-gradient(to bottom, yellow, green); // 使用关键字指定方向
  background-image: linear-gradient(180deg, yellow, green); // 使用角度指定方向
  background-image: linear-gradient(to top, green, yellow);
  background-image: linear-gradient(to bottom, yellow 0%, green 100%); // 指定颜色断点
  • 栗子二:当然多个颜色断点也可以
background-image: linear-gradient(to bottom, #FF0000 14.28%, #FFA500 14.28%, #FFA500 28.57%, #FFFF00 28.57%, #FFFF00 42.85%, #008000 42.85%, #008000 57.14%, #0000FF 57.14%, #0000FF 71.42%, #4B0082 71.42%, #4B0082 85.71%, #800880 85.71%, #800880 100%);

这个例子还有个小技巧,Gradient 中两个不同颜色间默认是渐变的,但如果我们需要做出图中这种颜色明显变化的效果(锐变),就可以用同一个位置不同颜色的方式实现。

  • 栗子三:在颜色上设置透明度渐变
.gradient-1 {
  background-image: url(http://a57.foxnews.com/global.fncstatic.com/static/managed/img/fn2/876/493/EmmaWatsonBrown.jpg);
  background-size: 100% 100%;
}
 
.gradient-2 {
  background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1)), url(http://a57.foxnews.com/global.fncstatic.com/static/managed/img/fn2/876/493/EmmaWatsonBrown.jpg) no-repeat;
  background-size: 100% 100%;
}

这个效果其实是利用了 CSS3 的多背景语法

径向渐变(radial-gradient)

background-image: radial-gradient(辐射的半径大小 中心的位置, 起始颜色, 终止颜色);
background-image: radial-gradient(100px at center, yellow, green);

radial gradient 其实就是颜色从一个点以同心圆或者椭圆向外渐变。

简化版语法如下:

radial-gradient() = radial-gradient(
  [ || ]? [ at ]? ,
  <color-stop-list>
)
  • position 用来指定渐变圆心的位置,默认为 center,赋值规则与 background-positon 的类似;

  • ending-shape 可以是 circle 或者 elipse,如果省略,则默认值与 size 相关,size 指定一个值就是圆形,否则是椭圆;

  • size 可以是具体的值,也可以用关键字指定,默认值是 farthest-corner。如果是具体值,圆形需要一个数值,椭圆需要两个。关键字则包括:

    • closest-side 指 gradient box 某一边到盒子中心最近的距离;
    • farthest-side 指 gradient box 某一边到盒子中心最远的距离;
    • closest-corner 指 gradient box 某一顶点到盒子中心最近的距离;
    • farthest-corner 指 gradient box 某一顶点到盒子中心最远的距离;
  • color-stop-list 与 linear-gradient 类似

注意:

  • size 的数值不能是负数
  • W3C 规范规定,百分比的数值只能用于椭圆,不能用于圆形。
  • position 的值可以是负数

栗子一:以下几种写法是等价的

.gradient-1 {
  background-image: radial-gradient(yellow, green);
}
 
.gradient-2 {
  background-image: radial-gradient(ellipse at center, yellow 0%, green 100%);
}
 
.gradient-3 {
  background-image: radial-gradient(farthest-corner at 50% 50%, yellow, green);
}
 
.gradient-4 {
  background-image: radial-gradient(ellipse farthest-corner at 50% 50%, yellow, green);
}

栗子二:看下 size 各种关键字的效果

.gradient-1 {
  background-image: radial-gradient(circle closest-side at 30% 50%, yellow, green);
}
 
.gradient-2 {
  background-image: radial-gradient(circle farthest-side at 30% 50%, yellow, green);
}
 
.gradient-3 {
  background-image: radial-gradient(circle closest-corner at 30% 50%, yellow, green);
}
 
.gradient-4 {
  background-image: radial-gradient(circle farthest-corner at 30% 50%, yellow, green);
}

圆心没设置在中心是因为矩形的对角线相等且平分,所以 closest-corner = farthest-corner,就没法比较差异了。

重复渐变(Repeating Gradients)

基本语法与上面的线性渐变和径向渐变类似,就是增加了重复的特性。

重复的规则简单说就是用最后一个颜色断点的位置减去第一个颜色断点位置的距离作为区间长度,不断的重复。比如 repeating-linear-gradient(red 10px, blue 50px) 就相当于 linear-gradient(…, red -30px, blue 10px, red 10px, blue 50px, red 50px, blue 90px, …)

至于首尾颜色距离为 0 等特殊情况,这里就不细说 了,可以到 W3C 规范自行研究。

background-position

background-position 主要是用来指定背景图片在容器中的位置。大家较为熟悉的是,背景图片左上角(顶点)相对于容器左上角计算

background-position: 100px 200px;		/* 向右偏移量 向下偏移量 */

不过,有的时候,希望背景图片能相对于容器右侧边缘或底部边缘计算

要让背景图片距离容器右侧边缘和底部边缘都是 50px 。针对这样的场景,你或许首先会想到使用容器大小和背景图片大小进行计算,得出距离顶部和左侧边缘的距离,然后将计算出来的值运用于 background-position 中。当然,熟悉 CSS 的同学或许会想到使用 calc() 函数:

:root {
    --xPosition: 50px;
    --yPosition: 50px;
}

.container {
    background-position: calc(100% - var(--xPosition)  calc(100% - var(--yPosition)))
}

使用 calc() 动态计算要比通过容器和背景图片尺寸大小计算方便得多。事实上呢?background-position 提供了另一种特性,我们可以通过关键词 toprightbottomleft 来指定方向。比如我们熟悉的用法 :

background-position: 50px 50px;

// 相当于
background-position: top 50px  left 50px;

那么,我们要实现上图的效果,就可以使用 rightbottom 关键词,让事情变得非常简单:

:root {
	--xPosition: 50px;
    --yPosition: 50px;
}

.container {
	background-position: right var(--xPosition)  bottom var(--yPosition);
}

background-position 使用还有一个小细节需要注意,即 background-position 采用百分比值。因为使用百分比值的 background-position 计算会相对于其他单位值复杂:

背景图片原始尺寸是 100px x 100px ,容器的尺寸(使用该背景图片的元素)是 410px x 210px ,如果使用 background-position: 75% 50% 时,它的计算如下:

/** background-position的x轴坐标 **/
x = (容器宽度 - 背景图片宽度) x background-position的x坐标的百分比值 = (410 - 100) x 75% = 232.5px

/** background-position的y轴坐标 **/
y = (容器高度 - 背景图片高度) x background-position的y坐标的百分比值 = (210 - 100) x 50% = 55px
复制代码

就该示例而言,background-position: 75% 50% 就相当于 bacckground-position: 232.5px 55px

background-size

设置背景图片的尺寸。

background-size: 500px 500px;
background-size: 50% 50%;	/* 相对于容器的大小 */
background-size: cover;
background-size: contain;
  • cover:图片始终填充满容器,且保证长宽比不变。图片如果有超出部分,则超出部分会被隐藏。
  • contain:将图片完整地显示在容器中,且保证长宽比不变。可能会导致容器的部分区域留白

background-repeat

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

background-repeat: no-repeat;

它可以取以下值。

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

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

.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-attachment

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

background-attachment: scroll;
  • scroll:这个是默认值,表示背景图片绑定区块的内容,会随着视口滚动条滚动,但不会随着区块滚动条滚动。
  • fixed:背景图片绑定视口,不会随着视口滚动条和区块滚动条滚动。
  • local:背景图片会随着视口滚动条和区块滚动条滚动。

background-origin

控制背景从什么地方开始显示。

background-origin: padding-box;		/* 从 padding-box 开始显示背景图 */
background-origin: border-box;		/* 从 border-box 开始显示背景图  */
background-origin: content-box;		/* 从 content-box 区域开始显示背景图  */

background-clip

设置元素的背景(背景图片或颜色)是否延伸到边框下面。

background-clip: content-box;
  • border-box:超出 border-box 的部分,将裁剪掉

  • padding-box:超出 padding-box 的部分,将裁剪掉

  • content-box:超出 content-box 的部分,将裁剪掉

background

background简写属性在一个声明中可设置所有的背景属性。

background: bg-color || bg-image || bg-position [ / bg-size]? || bg-repeat || bg-attachment || bg-origin || bg-clip

可设置属性如下:

  • background-color: 指定背景颜色。
  • background-image: 设置背景图像, 可以是真实的图片路径, 也可以是创建的渐变背景;
  • background-position: 设置背景图像的位置;
  • background-size: 设置背景图像的大小;
  • background-repeat: 指定背景图像的铺排方式;
  • background-attachment: 指定背景图像是滚动还是固定;
  • background-origin: 设置背景图像显示的原点[background-position相对定位的原点];
  • background-clip: 设置背景图像向外剪裁的区域;
  • background: transparent; :可以设置完全透明。

background是一个复合属性, 可设置多组属性, 每组属性间使用逗号分隔, 其中背景颜色不能设置多个且只能放在最后一组

background: url(image1.jpg) no-repeat 100px 100px fixed
			url(image2.png) no-repeat right top,
            url(image3.png) no-repeat right bottom,
            url(image4.png) no-repeat left bottom,
            url(image5.png) no-repeat center red;

可以给一个盒子同时设置多个背景,用以逗号隔开即可。

background-blend-mode

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

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

mix-blend-mode

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

filter

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

  • none:默认值,没有效果。
  • url():引用定义在SVG文件中的滤镜
  • blur(px):给图像设置高斯模糊,参数为模糊半径,如果没有设定值,则默认是0;这个参数可设置css长度值,但不接受百分比值。
  • brightness(%):给图片应用一种线性乘法,使其看起来更亮或更暗,0%为全黑,100%为原始亮度,值超过100%也是可以的,图像会比原来更亮。如果没有设定值,默认是1。
  • contrast(%):对比度,0%为全黑,100%为原始对比度,值可以超过100%,意味着会运用更低的对比。若没有设置值,默认是1。
  • drop-shadow(h-shadow v-shadow blur spread color):阴影效果,设置同box-shadow接近
  • grayscale(%):将图像转换为灰度图像,0%为原始色彩,100%为完全灰度。若未设置,值默认是0。
  • hue-rotate(deg): 给图像应用色相旋转,0为原始色调,360为色彩轮旋转一周后回到原色调。若值未设置,默认值是0deg。
  • invert(%):色调分离,负片效果,0%为原始效果,100%为完全负片效果。若值未设置,值默认是0。
  • opacity(%):透明度,0%为完全透明,100%为完全不透明。若值未设置,值默认是1。该函数与已有的opacity属性很相似,不同之处在于通过filter,一些浏览器为了提升性能会提供硬件加速。
  • saturate(%):饱和度,0%为完全不饱和,100%为完全饱和。超过100%的值是允许的,则有更高的饱和度。若值未设置,值默认是1。
  • sepia(%):加入褐色色调,作旧效果,0%为原始效果,100%为完全作旧。若未设置,值默认是0。
filter: url(resources.svg#c1);
filter: blur(5px);
filter: brightness(0.5);
filter: contrast(200%);
filter: drop-shadow(16px 16px 10px black);
filter: grayscale(50%);
filter: hue-rotate(90deg);
filter: invert(100%);
filter: opacity(50%);
filter: saturate(200%);
filter: sepia(100%);

多个滤镜可以联合使用。

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

动画

transition

transition: [property] [transition-duration] [transition-timing-function] [transition-delay];

基本用法

在CSS 3引入Transition(过渡)这个概念之前,CSS是没有时间轴的。也就是说,所有的状态变化,都是即时完成。

下面是代码,相当简单。当鼠标放置于缩略图之上,缩略图会迅速变大。注意,缩略图的变大是瞬间实现的。

img{
    height:15px;
    width:15px;
}

img:hover{
    height: 450px;
    width: 450px;
}

transition的作用在于,指定状态变化所需要的时间。

img{
    transition: 1s;
}

上面代码指定,图片放大的过程需要1秒。

我们还可以指定transition适用的属性,比如只适用于height。

img{
    transition: 1s height;
}

这样一来,只有height的变化需要1秒实现,其他变化(主要是width)依然瞬间实现,效果如下。

transition-delay

在同一行transition语句中,可以分别指定多个属性。

img{
    transition: 1s height, 1s width;
}

但是,这样一来,height和width的变化是同时进行的,跟不指定它们没有差别。

我们希望,让height先发生变化,等结束以后,再让width发生变化。实现这一点很容易,就是为width指定一个delay参数。

img{
    transition: 1s height, 1s 1s width;
}

上面代码指定,width在1秒之后,再开始变化,也就是延迟(delay)1秒。

delay的真正意义在于,它指定了动画发生的顺序,使得多个不同的transition可以连在一起,形成复杂效果。

transition-timing-function

transition的状态变化速度(又称timing function),默认不是匀速的,而是逐渐放慢,这叫做ease。

img{
    transition: 1s ease;
}

除了ease以外,其他模式还包括

(1)linear:匀速

(2)ease-in:加速

(3)ease-out:减速

(4)cubic-bezier函数:自定义速度模式

最后那个cubic-bezier,可以使用工具网站来定制。

img{
    transition: 1s height cubic-bezier(.83,.97,.05,1.44);
}

上面的代码会产生一个最后阶段放大过度、然后回缩的效果。

transition的各项属性

transition的完整写法如下。

img{
    transition: 1s 1s height ease;
}

这其实是一个简写形式,可以单独定义成各个属性。

img{
    transition-property: height;
    transition-duration: 1s;
    transition-delay: 1s;
    transition-timing-function: ease;
}

transition的使用注意

(1)目前,各大浏览器(包括IE 10)都已经支持无前缀的transition,所以transition已经可以很安全地不加浏览器前缀。

(2)不是所有的CSS属性都支持transition,完整的列表查看这里,以及具体的效果

(3)transition需要明确知道,开始状态和结束状态的具体数值,才能计算出中间状态。比如,height从0px变化到100px,transition可以算出中间状态。但是,transition没法算出0px到auto的中间状态,也就是说,如果开始或结束的设置是height: auto,那么就不会产生动画效果。类似的情况还有,display: none到block,background: url(foo.jpg)到url(bar.jpg)等等。

transition的局限

transition的优点在于简单易用,但是它有几个很大的局限。

(1)transition需要事件触发,所以没法在网页加载时自动发生。

(2)transition是一次性的,不能重复发生,除非一再触发。

(3)transition只能定义开始状态和结束状态,不能定义中间状态,也就是说只有两个状态。

(4)一条transition规则,只能定义一个属性的变化,不能涉及多个属性。

CSS Animation就是为了解决这些问题而提出的。

Animation

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

基本用法

首先,CSS Animation需要指定动画一个周期持续的时间(animation-duration),以及动画效果的名称(animation-name)。

div:hover {
  animation: 1s rainbow;
}

上面代码表示,当鼠标悬停在div元素上时,会产生名为rainbow的动画效果,持续时间为1秒。为此,我们还需要用keyframes关键字,定义rainbow效果。

@keyframes rainbow {
  0% { background: #c00; }
  50% { background: orange; }
  100% { background: yellowgreen; }
}

上面代码表示,rainbow效果一共有三个状态,分别为起始(0%)、中点(50%)和结束(100%)。如果有需要,完全可以插入更多状态。

默认情况下,动画只播放一次。加入infinite关键字,可以让动画无限次播放。

div:hover {
  animation: 1s rainbow infinite;
}

也可以指定动画具体播放的次数,比如3次。

div:hover {
  animation: 1s rainbow 3;
}

这里还有一个心脏跳动的例子,可供参考。源码如下:

@keyframes pound {
  from { transform: none; }
  to { transform: scale(1.2); }
}

.heart {
  animation: pound .3s infinite;
}

animation-fill-mode

动画结束以后,会立即从结束状态跳回到起始状态。如果想让动画保持在结束状态,需要使用animation-fill-mode属性。

div:hover {
  animation: 1s rainbow forwards;
}

forwards表示让动画停留在结束状态。

animation-fill-mode还可以使用下列值:

  • none:默认值,回到动画没开始时的状态。
  • backwards:让动画回到第一帧的状态。
  • both: 根据animation-direction 轮流应用forwards和backwards规则。

animation-direction

动画循环播放时,每次都是从结束状态跳回到起始状态,再开始播放。animation-direction属性,可以改变这种行为。

下面看一个例子,来说明如何使用animation-direction。假定有一个动画是这样定义的。

@keyframes rainbow {
  0% { background-color: yellow; }
  100% { background: blue; }
}

默认情况是,animation-direction等于normal。

div:hover {
  animation: 1s rainbow 3 normal;
}

此外,还可以等于取alternate、reverse、alternate-reverse等值。它们的含义见下图(假定动画连续播放三次)。

img

简单说,animation-direction指定了动画播放的方向,最常用的值是normal和reverse。

animation的各项属性

同transition一样,animation也是一个简写形式。

div:hover {
  animation: 1s 1s rainbow linear 3 forwards normal;
}

这是一个简写形式,可以分解成各个单独的属性。

div:hover {
   animation-name: rainbow;
   animation-duration: 1s;
   animation-timing-function: linear;
   animation-delay: 1s;
   animation-fill-mode:forwards;
   animation-direction: normal;
   animation-iteration-count: 3;
}
  • animation-timing-function:定义动画的速度曲线。 速度曲线决定了动画从一套 CSS 样式变为另一套所用的时间。浏览器从一个状态向另一个状态过渡,默认是平滑过渡。

    • ease:快速加速,然后逐渐减速,CSS 的默认值。

    • ease-out:开始时速度最快,然后逐渐慢下来,适用于进入页面显示的元素。

    • ease-in:逐渐加速,结尾时变快,适用于退出页面显示的元素。

    • ease-in-out:加速然后变慢,与ease相似,但开始时加速较慢,适合那些在页面有着明确开始和结束的动画。

    • linear:线性运行,各个时刻速度都一样。

    • steps():实现分步过渡。这里有一个非常神奇的例子,可以看到steps函数的用处。源码如下:

      /**
       * Typing animation with pure CSS. 
       * Works best in browsers supporting the ch unit.
       */
      
      @keyframes typing { from { width: 0; } }
      @keyframes blink-caret { 50% { border-color: transparent; } }
      
      h1 { 
      	font: bold 200% Consolas, Monaco, monospace;
      	border-right: .1em solid;
      	width: 16.5em; /* fallback */
      	width: 30ch; /* # of chars */
      	margin: 2em 1em;
      	white-space: nowrap;
      	overflow: hidden;
      	animation: typing 20s steps(30, end), /* # of steps = # of chars */
      	           blink-caret .5s step-end infinite alternate;
      }
      
  • animation-delay:动画效果多少秒后开始,默认为0

  • animation-iteration-count:动画重复的次数,可以指定为一个整数,表示多少次,默认值是infinite关键字,表示无限次。

keyframes的写法

keyframes关键字用来定义动画的各个状态,它的写法相当自由。

@keyframes rainbow {
  0% { background: #c00 }
  50% { background: orange }
  100% { background: yellowgreen }
}

0%可以用from代表,100%可以用to代表,因此上面的代码等同于下面的形式。

@keyframes rainbow {
  from { background: #c00 }
  50% { background: orange }
  to { background: yellowgreen }
}

如果省略某个状态,浏览器会自动推算中间状态,所以下面都是合法的写法。

@keyframes rainbow {
  50% { background: orange }
  to { background: yellowgreen }
}

@keyframes rainbow {
  to { background: yellowgreen }
}

甚至,可以把多个状态写在一行。

@keyframes pound {
  fromto { transform: none; }
  50% { transform: scale(1.2); }
}

animation-play-state

有时,动画播放过程中,会突然停止。这时,默认行为是跳回到动画的开始状态。

如果想让动画保持突然终止时的状态,就要使用animation-play-state属性。

div {
  animation: spin 1s linear infinite;
  animation-play-state: paused;
}

div:hover {
  animation-play-state: running;
}

上面的代码指定,没有鼠标没有悬停时,动画状态是暂停;一旦悬停,动画状态改为继续播放。效果如下。

贝塞尔曲线

animation-timing-function 除了预定义值之外,CSS 还提供了贝塞尔曲线(Bezier curves)来更细致地控制动画的速度曲线。

可以用 cubic-bezier 来定义贝塞尔曲线。 曲线的形状代表了动画的速度。 曲线在 1 * 1 的坐标系统内, 其中 X 轴代表动画的时间间隔(类似于时间比例尺),Y 轴代表动画的改变。

cubic-bezier 函数包含了 1 * 1 网格里的4个点:p0p1p2p3。 其中 p0p3 是固定值,代表曲线的起始点和结束点,坐标值依次为 (0, 0) 和 (1, 1)。 你只需设置另外两点的 x 值和 y 值,设置的这两点确定了曲线的形状从而确定了动画的速度曲线。 在 CSS 里面通过 (x1, y1, x2, y2) 来确定 p1p2

animation-timing-function: cubic-bezier(x1, y1, x2, y2);

通俗的讲,将一条直线放在范围只有 1 的坐标轴中,并从中间拿 p1p2 两个点来拉扯(X 轴的取值区间是 [0, 1],Y 轴任意),最后形成的曲线就是动画的贝塞尔速度曲线。

transform

注意:给 div 元素添加 transform 也会影响这个 div 包裹的子元素。

2D 转换

同时设置多个不同的CSS变换(transform)属性时,中间用空格隔开即可, 执行顺序是从后向前的。

transform: rotate(360deg) scale(2,2) skew(10deg,5deg);
缩放

transform 里面的 scale() 函数可以用来改变元素的显示比例。

transform: scale(x, y);
transform: scale(2, 0.5);

x 表示水平方向的缩放倍数。y表示垂直方向的缩放倍数。如果只写一个值就是等比例缩放。大于1表示放大,小于1表示缩小。不能为百分比。

位移
transform: translate(水平位移, 垂直位移);
transform: translate(-50%, -50%);

参数为百分比,相对于自身移动,即指的是盒子高度。正值向右和向下,负值向左和向上。如果只写一个值,则表示水平移动。

  • translateY(ty) 对应 translate(0, ty)translate3d(0, ty, 0)
  • translateX(tx)等同于 translate(tx, 0) 或者 translate3d(tx, 0, 0)
旋转
transform: rotate(角度);
transform: rotate(45deg);

正值顺时针,负值逆时针。默认是以盒子的正中心为坐标原点的。如果想改变旋转的坐标原点,可以用transform-origin属性:

transform-origin: 水平坐标 垂直坐标;
transform-origin: 50px 50px;
transform-origin: center bottom;
倾斜

skew() 函数定义了一个元素在二维平面上的倾斜转换。

skew() 函数指定一个或两个参数,它们表示在每个方向上应用的倾斜量。

skew(ax)
skew(ax, ay)
  • ax 是一个 <angle>,表示用于沿横坐标扭曲元素的角度。
  • ay 是一个 <angle> ,表示用于沿纵坐标扭曲元素的角度。如果未定义,则其默认值为0,导致纯水平倾斜。

transform 属性 skewX():它使选择的元素沿着 X 轴(横向)倾斜指定的角度。

p {
  transform: skewX(-32deg);
}

skewY 属性使指定元素沿 Y 轴(垂直方向)倾斜指定角度。

图形变换:详解Transform:skew函数的数学原理

3D 转换

  • 3D坐标系(左手坐标系):伸出左手,让拇指和食指成“L”形,大拇指向右,食指向上,中指指向前方。拇指、食指和中指分别代表X、Y、Z轴的正方向,这样我们就建立了一个左手坐标系。浏览器的X轴就是屏幕左到右, Y轴就是屏幕上到下,Z轴就是屏幕到用户。
  • 旋转的方向(左手法则):左手握住旋转轴,竖起拇指指向旋转轴的正方向,正向就是其余手指卷曲的方向。
旋转
transform: rotate(360deg);		/* 绕 Z 轴旋转360度 */
transform: rotateX(360deg);    /* 绕 X 轴旋转360度 */
transform: rotateY(360deg);    /* 绕 Y 轴旋转360度 */
transform: rotateZ(360deg);    /* 绕 Z 轴旋转360度 */

要看到 X轴,Y轴旋转效果,须加透视 perspective

移动
transform: translateX(100px);    /* 沿着 X 轴移动 */
transform: translateY(360px);    /* 沿着 Y 轴移动 */
transform: translateZ(360px);    /* 沿着 Z 轴移动 */

要看 Z轴移动效果须加透视 perspective

透视

电脑显示屏是一个 2D 平面,图像之所以具有立体感(3D效果),其实只是一种视觉呈现,通过透视可以实现此目的。

perspective: 500px;
transform: perspective(0);
  • 作为一个属性,设置给父元素,作用于所有3D转换的子元素
  • 作为 transform 属性的一个值,做用于元素自身。
3D 呈现

3D元素构建是指某个图形是由多个元素构成的,可以给这些元素的父元素设置transform-style: preserve-3d来使其变成一个真正的3D图形。

transform-style: preserve-3d;     /* 让子盒子位于三维空间里 */
transform-style: flat;            /* 让子盒子位于此元素所在的平面内(子盒子被扁平化) */

多栏式布局

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

基本用法

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

div {
  column-count:4;
}

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

div {
  column-width:100px;
}

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

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

间隔

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

  • column-gap:间隔宽度
  • column-rule-color:间隔线的颜色
  • column-rule-style:间隔线的样式,比如dasheddotted等,默认是solid
  • column-rule-width:间隔线本身的宽度
  • column-rule:column-rule-colorcolumn-rule-stylecolumn-rule-width这三个属性的快捷形式。
div {
	column-gap: 20px;
	column-rule: 2px solid #33c;
}

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

column-span

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

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:子元素后方强制插入一个栏断点

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 }

表格布局可以很简单地做到垂直居中。

div {
  height: 200px;
  display:table-cell;
  vertical-align: middle;
}

这种写法相比下面的写法,更容易理解。

div {
  height: 200px;
  position: relative;
  top: 50%;
  transform: translateY(-50%);
}

表格布局的另一个用途是,让页尾总是显示在浏览器的最低部,即使页面高度不足一页。

/*
  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;
}

基于上面这种底部固定的技巧,可以使用表格布局,完成圣杯布局,即页面从上到下分成页首 + 内容 + 页尾三部分,其中内容部分又分成左边栏和右边栏。

/*
  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使得该行显示在表格的底部。

Flex 布局

2009年,W3C 提出了一种新的方案——Flex 布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。

Flex 布局简介

Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。

.box{
  display: flex;		/* block */
}

.box{
  display: inline-flex;		/* inline */
}

任何一个容器都可以指定为 Flex 布局。设为 Flex 布局以后,子元素的floatclearvertical-align属性将失效。

我想,设定 flex 后,父元素的内盒子变为 flex,子元素的外盒子变为 flex,但是父元素的外盒子,子元素的内盒子还是原来的盒子。

基本概念

采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。

容器默认存在两根轴:主轴(main axis)和交叉轴(cross axis)。

如果把 flex-direction属性设为 row,它的子元素会从左到右逐个排列; 如果把 flex-direction属性设为 column,它的子元素会从上到下逐个排列。 子元素排列的方向被称为 main axis(主轴)。 对于 row,主轴水平贯穿每一个项目; 对于column,主轴垂直贯穿每一个项目。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end

Flex 容器中,与主轴垂直的叫做 cross axis(交叉轴)。 row的交叉轴是垂直的,column的交叉轴是水平的。交叉轴的开始位置叫做cross start,结束位置叫做cross end

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

容器的属性

flex-direction

flex-direction属性决定主轴的方向(即项目的排列方向)。

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

  • column-reverse:主轴为垂直方向,起点在下沿。
  • column:主轴为垂直方向,起点在上沿。
  • row(默认值):主轴为水平方向,起点在左端。
  • row-reverse:主轴为水平方向,起点在右端。

flex-wrap

默认情况下,flex 容器会调整项目大小,把它们都塞到一起。 对于row来说,所有项目都会在一条直线上。flex-wrap属性定义,如果一条轴线排不下,如何换行。这意味着多出来的子元素会被移到新的行或列。 换行发生的断点由子元素和容器的大小决定。

.box{
  flex-wrap: nowrap | wrap | wrap-reverse;
}
  • nowrap(默认):不换行。

  • wrap:换行,row从上到下排,column从左到右排。

  • wrap-reverse:换行,row从下到上排,column从右到左排。

flex-flow

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

.box {
    flex-flow: flex-direction flex-wrap;
}

justify-content

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

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

  • flex-start(默认值): 对row来说是把项目移至左边, 对于column是把项目移至顶部。
  • flex-end:对row来说是把项目移至右边, 对于column是把项目移至底部。
  • center: flex 子元素在 flex 容器中居中排列。
  • space-between:项目间保留一定间距地沿主轴居中排列。 第一个和最后一个项目被放置在容器边沿。 例如,在行中第一个项目会紧贴着容器左边,最后一个项目会紧贴着容器右边,然后其他项目均匀排布。
  • space-around:与space-between相似,但头尾两个项目不会紧贴容器边缘,所有项目之间的空间均匀排布。每个项目两侧的间隔相等,所以,项目之间的间隔比项目与边框的间隔大一倍。

align-items

align-items属性定义项目在交叉轴上如何对齐。 对row来说,定义的是元素的上下对齐方式; 对column来说,是定义元素的左右对齐方式。

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

  • flex-start:对row来说,把项目移至容器顶部; 对column来说,是把项目移至容器左边。
  • flex-end:对row来说,把项目移至容器底部; 对column来说,把项目移至容器右边。
  • center:对row来说,垂直居中(项目距离顶部和底部的距离相等); 对column来说,水平居中(项目距离左边和右边的距离相等)。
  • baseline: 沿项目的第一行文字的基线对齐。
  • stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

align-content

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

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

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

项目的属性

order

order属性定义项目的排列顺序。默认情况下,项目排列顺序与源 HTML 文件中顺序相同。 这个属性接受数字作为参数,可以使用负数。数值越小,排列越靠前,默认为0。

.item {
  order: integer;
}

flex-grow

flex-grow 会在容器太大时对子元素作出调整。默认为0,即如果存在剩余空间,也不放大。

.item {
  flex-grow: number; /* default 0 */
}

如果所有项目的flex-grow属性都为1,则它们将等分剩余空间。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。

flex-shrink

flex-shrink属性定义了项目的缩小比例,默认为1。使用之后,如果 flex 容器太小,则子元素会自动缩小。当容器的宽度小于里面所有子元素的宽度之和时,所有子元素都会自动压缩。

.item {
  flex-shrink: number; /* default 1 */
}

子元素的 flex-shrink 接受数值作为属性值。 数值越大,则该元素与其他元素相比会被压缩得更厉害。

如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。

负值对该属性无效。

flex-basis

flex-basis 属性定义了在使用 CSS 的 flex-shrinkflex-grow 属性对元素进行调整前,元素的初始大小。flex-basis 属性的单位与其他表示尺寸的属性的单位一致(pxem% 等)。 如果值为 auto,则项目的尺寸随内容调整。

.item {
  flex-basis: length | auto; /* default auto */
}

flex

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

.item {
  flex: none | flex-grow flex-shrink flex-basis
}

该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。

align-self

align-self属性允许你调整单个子元素自己的对齐方式,而不会影响到全部子元素。 因为 floatclearvertical-align 等调整对齐方式的属性都不能应用于 flex 子元素,所以这个属性显得十分有用。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch

align-self 可设置的值与 align-items 的一样,并且它会覆盖 align-items 所设置的值。

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

Grid 布局

概述

网格布局(Grid)它将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局。

Flex 布局是轴线布局,只能指定"项目"针对轴线的位置,可以看作是一维布局。Grid 布局则是将容器划分成"行"和"列",产生单元格,然后指定"项目所在"的单元格,可以看作是二维布局。Grid 布局远比 Flex 布局强大。

相对于 flex 布局,为让元素对齐,你需要一种二维布局的方法,你希望通过行和列来控制对齐。

除了一维和二维布局之间的区别,还有一种办法决定该使用弹性盒还是网格来布局。弹性盒从内容出发。一个使用弹性盒的理想情形是你有一组元素,希望它们能平均地分布在容器中。你让内容的大小决定每个元素占据多少空间。如果元素换到了新的一行,它们会根据新行的可用空间决定它们自己的大小。

网格则从布局入手。当使用CSS网格时,你先创建网格,然后再把元素放入网格中,或者让自动放置规则根据把元素按照网格排列。

基本概念

容器和项目

采用网格布局的区域,称为"容器"(container)。容器内部采用网格定位的子元素,称为"项目"(item)。

<div>
  <div><p>1</p></div>
  <div><p>2</p></div>
  <div><p>3</p></div>
</div>

上面代码中,最外层的<div>元素就是容器,内层的三个<div>元素就是项目。

Grid 布局只对项目生效。项目只能是容器的顶层子元素(直接后代元素),不包含项目的子元素,比如上面代码的<p>元素就不是项目。我们可以把某个子元素设置为网格,就会得到一个嵌套的网格。

标准中还提到了匿名网格项目。当有一些字符串或文本被包含在网格容器中,但却没有被其他元素包装,它们就会被创建为匿名网格项目。下面的例子,假设容器的类 grid 设置了 display: grid 属性,那么网格中就有三个网格项目,第一个就是匿名项目,因为它没有用标签分隔,所以会被自动定位规则处理。另2个放在 div 中的项目,它们可以被自动定位,也可以通过网格的定位属性来定位。

<div class="grid">
    I am a string and will become an anonymous item
    <div>A grid item</div>
    <div>A grid item</div>
</div>

匿名项目被自动定位是因为没有办法选定它们。所以如果在网格中有一些因故未被包装的文本,要小心它们可能会出现在网格的不可预期的位置,因为它们的位置完全靠自动定位规则来确定。

行和列

容器里面的水平区域称为"行"(row),垂直区域称为"列"(column)。

我们通过 grid-template-columnsgrid-template-rows 属性来定义网格中的行和列。这些属性定义了网格的行轨道和列轨道。一个网格轨道就是网格中任意两条线之间的空间。

单元格(网格单元)

一个网格单元是在一个网格元素中最小的单位, 从概念上来讲其实它和表格的一个单元格很像。

行和列的交叉区域,称为"单元格"(cell)。

正常情况下,n行和m列会产生n x m个单元格。比如,3行3列会产生9个单元格。

多个元素可以放置在网格单元格中,或者区域可以部分地彼此重叠。然后可以CSS中的z-index属性来控制重叠区域显示的优先级。

网格线

应该注意的是,当我们定义网格时,我们定义的是网格轨道,而不是网格线。Grid 会为我们创建编号的网格线来让我们来定位每一个网格元素。

网格中,假想的划分网格的水平线和垂直线,称为"网格线"(grid line)。水平网格线划分出行,垂直网格线划分出列。这些线在网格的左上角从 1 开始编号,垂直线向右、水平线向下累加计数。

正常情况下,n行有n + 1根水平网格线,m列有m + 1根垂直网格线,比如三行就有四根水平网格线。

隐式和显式网格

当我们创建上文中网格例子的时候,我们用 grid-template-columns 属性定义了自己的列轨道,但是却让网格按所需的内容创建行,这些行会被创建在隐式网格中。显式网格包含了你在 grid-template-columnsgrid-template-rows 属性中定义的行和列。如果你在网格定义之外又放了一些东西,或者因为内容的数量而需要的更多网格轨道的时候,网格将会在隐式网格中创建行和列。按照默认,这些轨道将自动定义尺寸,所以会根据它里面的内容改变尺寸。

你也可以在隐式网格中用 grid-auto-rowsgrid-auto-columns 属性来定义一个设置大小尺寸的轨道。

容器属性

display

display: grid指定一个容器采用网格布局。

div {
  display: grid;		/* block */
}

默认情况下,容器元素都是块级元素,但也可以设成行内元素。

div {
    display: inline-grid;		/* inline */
}

设为网格布局以后,容器子元素(项目)的floatdisplay: inline-blockdisplay: table-cellvertical-aligncolumn-*等设置都将失效。

grid-template-columns,grid-template-rows

容器指定了网格布局以后,接着就要划分行和列,明确网格的结构。grid-template-columns属性定义每一列的列宽,grid-template-rows属性定义每一行的行高。

.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-template-rows: 100px 100px 100px;
}


.container {
    display: grid;
    grid-template-columns: 33.33% 33.33% 33.33%;
    grid-template-rows: 33.33% 33.33% 33.33%;
}

上面代码指定了一个三行三列的网格。

在 CSS 网格中,可以使用绝对单位(如 px)或相对单位(如 em)来定义行或列的大小。%将列或行调整为它的容器宽度或高度的百分比,

fr 关键字

轨道可以使用任何长度单位进行定义。为了方便表示比例关系,网格布局提供了fr关键字(fraction 的缩写,意为"片段"),用于设置列或行占剩余空间的比例。如果两列的宽度分别为1fr2fr,就表示后者是前者的两倍。

.container {
  display: grid;
  grid-template-columns: 150px 1fr 2fr;
}

上面代码表示,第一列的宽度为150像素,第二列占剩余空间的宽度是第三列的一半。

auto 关键字

auto关键字设置列宽或行高自动等于它的内容的宽度或高度,

grid-template-columns: 100px auto 100px;

repeat()

可以使用repeat()函数,简化重复的值。

.container {
  display: grid;
  grid-template-columns: repeat(3, 33.33%);
  grid-template-rows: repeat(3, 33.33%);
}

repeat()接受两个参数,第一个参数是重复的次数(上例是3),第二个参数是所要重复的值。

repeat()重复某种模式也是可以的。

grid-template-columns: repeat(2, 100px 20px 80px);

上面代码定义了6列,第一列和第四列的宽度为100px,第二列和第五列为20px,第三列和第六列为80px。

minmax()

minmax()函数产生一个长度范围,表示长度就在这个范围之中。 它的作用是在网格容器改变大小时限制网格项的大小。它接受两个参数,分别为最小值和最大值。

grid-template-columns: 1fr 1fr minmax(100px, 1fr);

上面代码中,minmax(100px, 1fr)表示列宽不小于100px,不大于1fr

auto-fill 关键字

我们可以通过使用repeat方法,配合auto-fillauto-fit属性,创建类似弹性盒的效果,同时保证内容严格按照行和列的固定规则排列。

repeat 方法带有一个名为自动填充(auto-fill)的功能。 它的功能是根据容器的大小,尽可能多地放入指定大小的行或列。

.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, 100px);
}

上面代码表示每列宽度100px,然后自动填充,直到容器不能放置更多的列。

你可以通过结合 auto-fillminmax 来更灵活地布局。如果容器宽度不足以将所有网格项放在同一行,余下的网格项将会移至新的一行。

repeat(auto-fill, minmax(60px, 1fr));

上面的代码效果是这样:首先,列的宽度会随容器大小改变。其次,只要容器宽度不足以插入一个宽为 60px 的列,当前行的所有列就都会一直拉伸。

auto-fit 关键字

auto-fit 效果几乎和 auto-fill 一样。 不同点仅在于,当容器的大小大于各网格项之和时,auto-fill 会持续地在一端放入空行或空列,这样就会使所有网格项挤到另一边;而 auto-fit 则不会在一端放入空行或空列,而是会将所有网格项拉伸至合适的大小。

grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));

网格线的名称

在用 grid-template-rowsgrid-template-columns 属性定义网格时,可以为网格中的部分或全部网格线命名。

使用方括号,指定每一根网格线的名字,方便以后的引用。

.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根水平网格线。方括号里面依次是这八根线的名字。

为线命名的方法对于创建响应式页面特别有用,当需要在媒体查询中重新定义网格时,你就不需要通过修改线序号来改变布局,只要直接使用定义过的线名字就可以了,因为(即使在不同的布局中)线的名字总是相同的。

你可能想要给一条线命名多个名字。为了实现这个效果,只要把多个名字都写到方括号里,然后用空格分隔就行了:[sidebar-end main-start]。在引用时可以使用其中的任何一个名字。

在选择名字时,如果把一个区域周围的线都用 -start-end 作为后缀,网格就会为区域创建一个名字,名字就是后缀前的单词。不需要通过 grid-template-areas 来指定区域的位置,因为它已经被命名线约束好了。

.wrapper {
   display: grid;
   grid-template-columns: [main-start] 1fr [content-start] 1fr [content-end] 1fr [main-end];
   grid-template-rows: [main-start] 100px [content-start] 100px [content-end] 100px [main-end];
}

.thing {
  grid-area: content;
}

<div class="wrapper">
  <div class="thing">I am placed in an area named content.</div>
</div>

为了获得由命名的区域隐式创建命名线的能力,和由命名线隐式创建区域的能力,在创建网格布局时花一点时间来设计命名策略是非常值得的。慎重地选择名字对你和你的团队的意义在于,你创建网格的工作会变得容易,对其他使用和维护网格的人也大有益处。

可用 repeat() 定义相同名字的多条线

.wrapper {
      display: grid;
      grid-template-columns: repeat(12, [col-start] 1fr);
}

上面的例子创建了一个有 12 个等宽列的网格,在定义列轨道尺寸为 1fr 之前,也定义了网格线名字 [col-start],也就是说最终会创建一个 12 列的网格,每个 1fr 宽的列左侧的线都被命名为 col-start

如果要引用其他的同名线,就要加上序号。比如要定位项目从名为 col-start 的第1条线开始,到第5条线结束,应该这样写:

.item1 {
  grid-column: col-start / col-start 5
}

此处也可以使用 span 关键字。比如下一个项目的位置从名为 col-start 的第7条线开始,跨越3条线。

.item2 {
  grid-column: col-start 7 / span 3;
}

repeat 语法不仅可用于重复的单一轨道尺寸,也可以用于轨道列表。下面的代码创建了一个 8 个轨道的网格,在一个名为 col1-start1fr 窄轨道之后,跟着是一个名为 col2-start3fr 宽轨道。

.wrapper {
  grid-template-columns: repeat(4, [col1-start] 1fr [col2-start] 3fr);
}

如果 repeat 语法包含相邻的两条网格线,那它们会被合并,结果就像是不用 repeat 语法的创建的,所有的网格线都有多个名字。在下面的定义中,创建了四条 1fr 的轨道,每条轨道都有开始名和结束名。

.wrapper {
  grid-template-columns: repeat(4, [col-start] 1fr [col-end] );
}

如果不用 repeat 语法来定义它们,应该写成这样。

.wrapper {
  grid-template-columns: [col-start] 1fr [col-end col-start] 1fr [col-end col-start] 1fr  [col-end col-start] 1fr [col-end];
}

如果已经定义了一个轨道列表,接下来在使用 span 关键字定位项目时,不仅可以在 span 后面写一个行序数,还可以在 span 后面写一个命名线的行序数。

.wrapper {
  display: grid;
  grid-template-columns: repeat(6, [col1-start] 1fr [col2-start] 3fr);
}
.item1 {
  grid-column: col1-start / col2-start 2
}
.item2 {
  grid-row: 2;
  grid-column: col1-start 2 / span 2 col1-start;
}

row-gap,column-gap,gap

根据最新标准,上面三个属性名的grid-前缀已经删除,grid-column-gapgrid-row-gap写成column-gaprow-gapgrid-gap写成gap

row-gap属性设置行与行的间隔(行间距),column-gap属性设置列与列的间隔(列间距)。间距只出现在网格轨道与轨道之间,它们并不会出现在网格容器的四周。

container {
  row-gap: 20px;
  column-gap: 20px;
}

gap属性是column-gaprow-gap的合并简写形式,语法如下。

gap: row-gap column-gap;

如果 gap 只有一个值,那么这个值表示行与行之间、列与列之间的间距均为这个值。 如果有两个值,那么第一个值表示行间距,第二个值表示列间距。

间距使用的空间会在使用弹性长度fr的轨道的空间计算前就被留出来,间距的尺寸定义行为和普通轨道一致,但不同的是你不能向其中插入任何内容。

从基于线定位的角度来说,间距的行为就像是使基线变得特别宽。所有从这条线开始的东西会从间距结束的地方开始,你并不能定位也不能放任何东西到这个间距的空间中。如果你希望得到行为和轨道行为相似的间距,你当然可以定义轨道来作为间距。

grid-template-areas

网格元素可以向行或着列的方向扩展一个或多个单元,并且会创建一个网格区域。网格区域的形状应该是一个矩形 —— 也就是说你不可能创建出一个类似于“L”形的网格区域。

网格布局允许指定"区域"(area),并为该区域指定一个自定义名称。 一个区域由单个或多个单元格组成。grid-template-areas属性用于定义区域。

.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个单元格,然后将其定名为ai的九个区域,分别对应这九个单元格。

多个单元格合并成一个区域的写法如下。你需要要把区域名字在它的区域内重复写多次,中间用空格分隔。

grid-template-areas: 'a a a'
                     'b b b'
                     'c c c';

上面代码将9个单元格分成abc三个区域。

你也可以在 grid-template-areas 中添加额外的空格以便让列对齐。你可以看到我把 hdft 都跟 main 对齐了。

.wrapper {
    display: grid;
    grid-template-columns: repeat(9, 1fr);
    grid-auto-rows: minmax(100px, auto);
    grid-template-areas:
      "hd hd hd hd   hd   hd   hd   hd   hd"
      "sd sd sd main main main main main main"
      ".  .  .  ft   ft   ft   ft   ft   ft";
}

如果某些区域不需要利用,则使用"点"(.)来表示一个空单元格。

grid-template-areas: 'a . c'
                     'd . f'
                     'g . i';

为了让布局更整齐,可以使用多个 “.” 符号,如果在多个句点符号之间没有空格,那它们就会被计为一个单元格。用多个句点表示一个单元格的好处是对于复杂的布局来说很容易让行列对齐,这意味着你在 CSS 中看到的,就像是实际布局看起来那样。

注意,区域的命名会影响到网格线。每个区域的起始网格线,会自动命名为区域名-start,终止网格线自动命名为区域名-end。我们可以先用命名的模块区域来定义命名线,再用命名线定位项目。

grid-template-areas 的值必须是一个完整的网格,否则无效(即这个属性会被忽略掉),这意味着你应该让每一行都有相同数量的单元格,如果出现句点符就表示这个单元格将被留空。如果创建的区域不是矩形,也是无效的网格。

用媒体查询重新定义网格

.header {
    grid-area: hd;
}
.footer {
    grid-area: ft;
}
.content {
    grid-area: main;
}
.sidebar {
    grid-area: sd;
}

.wrapper {
    display: grid;
    grid-auto-rows: minmax(100px, auto);
    grid-template-columns: 1fr;
    grid-template-areas:
      "hd"
      "main"
      "sd"
      "ft";
}

@media (min-width: 500px) {
    .wrapper {
        grid-template-columns: repeat(9, 1fr);
        grid-template-areas:
          "hd hd hd hd   hd   hd   hd   hd   hd"
          "sd sd sd main main main main main main"
          "sd sd sd  ft  ft   ft   ft   ft   ft";
    }
}

@media (min-width: 700px) {
    .wrapper {
        grid-template-areas:
          "hd hd hd   hd   hd   hd   hd   hd hd"
          "sd sd main main main main main ft ft";
    }
}

我们可以发现使用网格布局时有非常多不同的方法来定位项目,这乍看起来有些过于复杂了,不过其实你不必把它们都用上。在实践中,我发现使用命名的模块区域是最直截了当的布局方法,它的可视化表示法能让你看到布局的效果,并且也容易在网格上移动项目。

grid-auto-flow

划分网格以后,容器的子元素会按照顺序,自动放置在每一个网格。默认的放置顺序是"先行后列",即先填满第一行,再开始放入第二行,即下图数字的顺序。

这个顺序由grid-auto-flow属性决定,默认值是row,即"先行后列"。也可以将它设成column,变成"先列后行"。

grid-auto-flow: column;

上面代码设置了column以后,放置顺序就变成了下图。

grid-auto-flow属性还可以设成row densecolumn dense。默认的grid-auto-flow: row情况下,会产生下面这样的布局。

这是因为对于自动定位的项目,如果轨道的大小不适合放入一个项目,这个项目就会被移动到下一行,直至它找到了可以容纳它的空间。也就是说除了我们明确定位过的项目,其他项目一般都会被网格自动处理并且保持它们在 DOM 中的顺序,这正是一般情况下我们想要的结果。比如你设计了一个表单,当然不想让标签和别的表单元素因为要填充缺口而使表单变得前后错乱。

不过有时候要布局的项目并没有逻辑顺序,所以我们希望能够创建一种没有缺口的布局。实现这个效果的方法是在网格容器的 grid-auto-flow 属性值中加入 dense 关键字。

grid-auto-flow 设为row dense,表示"先行后列",并且尽可能紧密填满,尽量不出现空格。

如果将设置改为column dense,表示"先列后行",并且尽量填满空格。

经过以上设置,网格就会回填缺口,以前网格会遗留下缺口,而现在它会为此前的缺口找到适合它的项目,然后把项目从 DOM 中拿出来再放到缺口中去。对于已经重新指定过顺序的网格,这样做不会改变项目之间原有的逻辑顺序,比如对于 Tab 键的顺序仍然与文档的顺序相同。此外你应该意识到,使用这个功能会让 DOM 中看到的顺序与实际显示的顺序不一致,它们两者之间的联系会被打破。

justify-items,align-items,place-items

网格布局方式下共有两条轴线用于对齐 —— 块方向的列轴文字方向的行轴。块方向的轴是采用块布局时块的排列方向。假设页面中有两个段落,其中一个显示在另一个下面,所以这个方向的轴被称为块方向的轴。在 CSS 网格布局规范中,它被称为列轴,因为这条轴的方向和列轨道是一致的。

行方向的轴与块方向的轴垂直,它的方向和普通文本的方向一致。在 CSS 网格规范中,它有时被称为行轴,因为这条轴的方向和行轨道是一致的。

我们可以把网格区域中的内容,以及网格轨道整体与这两条轴线对齐。所谓对齐,就是可以使项目在该轴上移动。

justify-items属性设置项目的行轴(左中右)对齐方式, align-items 用于控制项目在列轴(上中下)对齐方式。它们会将所有网格中所有的项目按所设置的方式对齐,相当于为所有的项目都设置了 justify-self 属性和 align-items 属性。

.container {
  justify-items: start | end | center | stretch;
  align-items: start | end | center | stretch;
}

  • start:对齐单元格的起始边缘。
  • end:对齐单元格的结束边缘。
  • center:单元格内部居中。
  • stretch(默认值):拉伸,占满单元格的整个宽度。

规范中对 align-self 的默认行为是拉伸(stretch),例外的情况是若项目具有固定宽高比,行为就改为与轴起点对齐(start)。此项例外的原因在于,如果把一个有固定宽高比的项目拉伸,将导致它扭曲变形。

place-items属性是align-items属性和justify-items属性的合并简写形式。

place-items: align-items justify-items;

如果省略第二个值,则浏览器认为与第一个值相等。

justify-content,align-content,place-content

设想这样一种场景,网格轨道整体占据的空间小于网格容器,那么就可以在容器中对齐网格轨道。针对块方向和文本方向的轴线,分别使用 align-content 对齐到块方向的轴线,使用 justify-content 对齐到文本方向的轴线。

.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;  
}
  • start - 对齐容器的起始边框。
  • end - 对齐容器的结束边框。
  • center - 容器内部居中。
  • stretch - 项目大小没有指定时,拉伸占据整个网格容器。
  • space-around - 每个项目两侧的间隔相等。所以,项目之间的间隔比项目与容器边框的间隔大一倍。
  • space-between - 项目与项目的间隔相等,项目与容器边框之间没有间隔。
  • space-evenly - 项目与项目的间隔相等,项目与容器边框之间也是同样长度的间隔。

下面的例子中,网格容器的宽高都是 500 像素,分别定义了行轨道和列轨道各 3 条,轨道尺寸为 100 像素,轨道间隙为 10 像素。可知,网格容器的块方向和文字方向都留有多余的空间。

属性 align-content 要设置在网格容器上,但它的效果却体现在内部的网格中。网格布局中默认的行为是对齐到起点(start),所以我们看到网格轨道整体居于网格的左上角,紧挨着起始的网格线:

.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3,100px);
  height: 500px;
  width: 500px;
  grid-gap: 10px;
  grid-template-areas:
    "a a b"
    "a a b"
    "c d d";
  align-content: space-between;
}
.item1 {
  grid-area: a;
}
.item2 {
  grid-area: b;
}
.item3 {
  grid-area: c;
}
.item4 {
  grid-area: d;
}

<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
</div>

有必要强调一下,这些与分配空间有关的值会使网格项目变大。如果项目跨越了多于一条轨道,因为轨道的间隙变大了,所以项目也变得更大了。因为精确尺寸的网格较为常用,所以如果你决定使用这些值,一定要确保其中的内容能够适应多出来的空间,或者在使用这些属性值时,把项目的对齐方式设置为对齐到起点(start),可能会比设置为拉伸(stretch)要好。

下图中的网格使用了 align-content 属性,左侧的值为 start,右侧的值为 space-between。观察 Item 1 和 Item 2,它们都跨越了两条行轨道,右侧的图因为增加了轨道间隙,所以它们占据的空间变大了:

在行轴上使用 justify-content 属性可以获得与在列轴上使用 align-content 相同的对齐效果。设置 justify-content 的值为 space-around,那些占据超过一列的项目将因此获得额外的空间。

place-content属性是align-content属性和justify-content属性的合并简写形式。

place-content: <align-content> <justify-content>

如果省略第二个值,浏览器就会假定第二个值等于第一个值。

grid-auto-columns,grid-auto-rows

为了精确地把项目摆放到网格中,CSS 网格布局规范还包含另外一组规则,用来约定当部分或全部子项目没有被明确指定位置时该如何处理。你会发现针对含有数个项目的网格,实际上最简单的方式就是使用自动定位。如果没有为项目指定位置信息,它们就会把自己摆放在网格中,每个单元格中放一个。

默认的流向是按行排列项目,网格会首先尝试在第1行的每个单元格中摆放项目。如果已经通过 grid-template-rows 属性创建了其他行,网格就会继续把项目摆放到这些行中。如果在显式的网格中没有足够的行用来摆放所有的项目,隐式的新行就会被创建出来。

在默认情况下,网格中被自动创建的隐式行的尺寸是自适应大小的,也就是说它们会包含所有属于它们的内容,而不会让内容溢出。

不过你可以通过 grid-auto-columns属性和grid-auto-rows属性 属性来控制它们的大小。

浏览器自动创建的隐式行的列宽和行高。它们的写法与grid-template-columnsgrid-template-rows完全相同。如果不指定这两个属性,浏览器完全根据项目的大小,决定新增网格的列宽和行高。

.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  grid-template-rows: 100px 100px 100px;
  grid-auto-rows: 50px; 
}

grid-template,grid

grid-template属性是grid-template-columnsgrid-template-rowsgrid-template-areas这三个属性的合并简写形式。

grid属性是grid-template-rowsgrid-template-columnsgrid-template-areasgrid-auto-rowsgrid-auto-columnsgrid-auto-flow这六个属性的合并简写形式。

从易读易写的角度考虑,还是建议不要合并属性,所以这里就不详细介绍这两个属性了。

项目属性

grid-column-start,grid-column-end,grid-row-start,grid-row-end

项目的位置是可以指定的,具体方法就是指定项目的四个边框,分别定位在哪根网格线。

  • grid-column-start属性:左边框所在的垂直网格线
  • grid-column-end属性:右边框所在的垂直网格线
  • grid-row-start属性:上边框所在的水平网格线
  • grid-row-end属性:下边框所在的水平网格线
.item-1 {
  grid-column-start: 2;
  grid-column-end: 4;
}

其他没有指定位置的项目,由浏览器自动布局,这时它们的位置由容器的grid-auto-flow属性决定。

在逐个定义元素后,注意,我们可以留空一些单元格。网格布局的一个优势是:无需给元素周围加上margin来阻止文档流自动填补空白,就能实现设计中的留白区域。

使用这四个属性,如果产生了项目的重叠,则使用z-index属性指定项目的重叠顺序。

这四个属性的值,除了指定为第几个网格线,还可以指定为网格线的名字。

.item-1 {
  grid-column-start: header-start;
  grid-column-end: header-end;
}

这四个属性的值还可以使用span关键字,表示"跨越",即左右边框(上下边框)之间跨越多少个网格。

.item-1 {
  grid-column-start: span 2;
}

这个元素就会从1号线开始,跨越3个线,到4号线结束。

.box1 {
    grid-column: 1;
    grid-row: 1 /  span 3;
}

这个元素会从4号线开始,跨越3个线到1号线。

.box1 {
    grid-column: 1;
    grid-row: span 3 / 4;
}

grid-column, grid-row

grid-column属性是grid-column-startgrid-column-end的合并简写形式,grid-row属性是grid-row-start属性和grid-row-end的合并简写形式。

.item {
  grid-column: <start-line> / <end-line>;
  grid-row: <start-line> / <end-line>;
}

.item-1 {
  grid-column: 1 / 3;
  grid-row: 1 / 2;
}

这两个属性之中,也可以使用span关键字,表示跨越多少个网格。

.item-1 {
  grid-column: 1 / span 2;
  grid-row: 1 / span 2;
}

斜杠以及后面的部分可以省略,默认跨越一个网格。

.item-1 {
  grid-column: 1;
  grid-row: 1;
}

grid-area

在为网格添加区域模板后,可以通过引用你所定义的区域的名称,将元素放入相应的区域。 grid-area属性指定项目放在哪一个区域。

.item-1 {
  grid-area: e;
}

grid-area属性还可用作grid-row-startgrid-column-startgrid-row-endgrid-column-end的合并简写形式,直接指定项目的位置。

.item {
  grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
}

.item-1 {
  grid-area: 1 / 1 / 3 / 3;
}

我们也可以从行和块结束线开始反方向计数,对于英语来说就是右端的列线和底端的行线。这些线会被记为 -1,然后你可以从这往前数,所以倒数第2条线会被记为 -2。值得注意的是最后一条线是指显式定义网格的最后一条线,即由 grid-template-columnsgrid-template-rows 定义的网格,并不把隐式定义网格的加入的行和列纳入考虑。

.item {
    grid-column: 1 / -1;
}

上面代码使一个元素跨越整个网格。

justify-self, align-self, place-self

justify-self属性设置项目的水平位置(左中右)对齐方式,跟justify-items属性的用法完全一致,但只作用于单个项目。

align-self属性设置项目的垂直位置(上中下)对齐方式,跟align-items属性的用法完全一致,也是只作用于单个项目。

.item {
  justify-self: start | end | center | stretch;
  align-self: start | end | center | stretch;
}
  • start:对齐单元格的起始边缘。
  • end:对齐单元格的结束边缘。
  • center:单元格内部居中。
  • stretch(默认值):拉伸,占满单元格的整个宽度。

place-self属性是align-self属性和justify-self属性的合并简写形式。

place-self: <align-self> <justify-self>;

如果省略第二个值,place-self属性会认为这两个值相等。

补充

网格布局中的盒模型对齐

What is difference between justify-self, justify-items and justify-content in CSS grid? - Stack Overflow

可以这样理解,grid 把容器分成一块一块的单元格,每一块单元格都可以放置一个项目。

justify-items 对齐的是项目。 justify-content 对齐的是单元格。grid-gap 是单元格之间间距。

另一个在网格区域内使项目对齐的方法是使用自动外边距。如果你曾经用过把容器的左右外边距都设置为 auto 以让页面布局显示在视口中间的方法的话,你肯定知道自动外边距能够消化掉全部的多余空间。当把两侧的外边距都设置为 auto 时,块元素就会被挤到中间,多余的空间则被留到两侧。

在下面的例子中,项目 item 1 的左外边距被设置成 auto,可以看到内容被推到了区域右侧,因为在为项目的内容分配了空间之后,自动外边距占据了剩余的空间:

.wrapper {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-template-rows: repeat(3,100px);
  height: 500px;
  width: 500px;
  grid-gap: 10px;
  grid-template-areas:
    "a a b"
    "a a b"
    "c d d";
}
.item1 {
  grid-area: a;
  margin-left: auto;
}
.item2 {
  grid-area: b;
}
.item3 {
  grid-area: c;
}
.item4 {
  grid-area: d;
}

<div class="wrapper">
  <div class="item1">Item 1</div>
  <div class="item2">Item 2</div>
  <div class="item3">Item 3</div>
  <div class="item4">Item 4</div>
</div>

可以理解为 justify-items: end;align-items: stretch;

作为包含块的网格容器

要使网格容器成为一个包含块,需要为容器增加 position 属性,并把它的值设置为 relative,就像为任何其他绝对定位的项目创建一个包含块一样。接下来,如果再把一个网格项目设置为 position: absolute,那么网格容器就成为包含块,或者如果该项目具有网格定位的属性,那么被放置的网格就成为包含块。

如果具有绝对定位的子元素的父级是网格容器,即使网格容器没有被设置为新的定位上下文(译注:即网格容器没有设置 position: relative 属性),该子元素仍会脱离文档流。定位是基于定位上下文的,和这其他布局方式没有区别。

此外,该项目不再被视为网格布局的一部分,当网格中其他元素调整尺寸或自动布局时,都与该项目无关。

网格和 display: contents

设置 display: contents 元素本身不会生成任何盒子,但其子元素和伪元素仍然会像平常一样生成盒子。为了生成盒子和布局,必须将元素视为已在文档树中被其子元素和伪元素替换。

如果将项目设置为 display: contents,通常自身的盒子会消失,子元素的盒子仍显示,就像子元素在文档树中上升了一层。这意味着一个网格项目的子元素可以成为网格项目。

这可以让项目嵌套到网格中,就好像它们是网格的一部分一样,并且可以用它解决 subgrid 值尚未实现时要解决的一些问题。 也可以在 flex 布局中用类似的方法通过 display: contents 使嵌套的项目成为 flex 项目。

比如:

<div class="outer">
  I'm, some content
  <div class="inner">I'm some inner content </div>
</div>

<style>
  .outer {
  	border: 2px solid lightcoral;
    background-color: lightpink;
    padding: 20px;
  }
  
  .innter {
  	background-color: #ffdb3a;
    padding: 20px;
  }
</style>

上面这个简单地示例代码,你将看到的效果如下:

如果我们在.outer 元素上显式设置 display: contents ,该元素本身不会被渲染,但子元素能够正常渲染: