深入了解CSS
CSS简要发展史
CSS 1:解决网页排版布局和装饰问题,第一个有“层叠(cascading)”概念的语言。
CSS 2:表现和内容分离
CSS 2.1:对CSS 2的修正、扩展代替CSS 2
CSS 3:规范模块化发展,样式丰富、酷炫,提高网站的可维护性以及性能速度。
基础知识:
层叠、优先级
常见 CSS 优先级误区
严格来说也不算是误区(错误),但是这种说法不够严谨。
通常我们聊到 CSS 规则的优先级,第一时间都会想到这个表,也就是给不同的 CSS 规则赋予不同的权重:
一个选择器的优先级可以说是由四个部分相加 (分量),可以认为是 个十百千 四位数的四个位数:
- 千位: 如果声明在 style 的属性(内联样式)则该位得一分。这样的声明没有选择器,所以它得分总是1000
- 百位: 选择器中包含ID选择器则该位得一分
- 十位: 选择器中包含类选择器、属性选择器或者伪类则该位得一分
- 个位:选择器中包含元素、伪元素选择器则该位得一分
总的来说是规则是:
内联 > id 选择器 > 类/属性/伪类选择器 > 标签元素/伪元素
上面的规则没有问题的。但是,注意,这里仅仅考虑的是页面作者定义的样式的优先级。首先,它并且没有包含 !important 规则。
其次,对于决定一个 CSS 样式的最终表现而言,还有非常重要的另外一个概念 -- 层叠。
层叠三大规则:
样式表来源:
注:由于transition和animation在不同浏览器和css规范规定的表现不一致,故忽略。
选择器优先级:
*内联样式不属于选择器,为了比较方便记忆,所以放在此做对比。
*:is() :not() :has() 本身不计入优先级,以参数中最高的优先级为准,:where()优先级为0
源码位置:
层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。它在CSS处于核心地位,CSS的全称层叠样式表正是强调了这一点。
那么什么所谓的多个源又表示什么呢?下面是影响层叠的五个源:
- 浏览器会有一个基本的样式表来给任何网页设置默认样式。这些样式统称用户代理样式
- 网页的作者可以定义文档的样式,这是最常见的样式表。大多数情况下此类型样式表会定义多个,它们构成网站的视觉和体验,即页面主题,可以理解为页面作者样式
- 读者,作为浏览器的用户,可以使用自定义样式表定制使用体验,可以理解为用户样式
- 动画(Animation),指使用 @Keyframes @规则定义状态间的动画,动画序列中定义关键帧的样式来控制CSS动画序列
- 过渡 (Transition)
层叠规范:
- 选择器尽量少用id
- 尽量不要用!important
- 自己的样式加载在引用库的样式后面
概括
- 决定一个元素的样式的最终表现,除了需要比较页面作者定义的样式的优先级之外,还需要比较样式的层叠顺序;
- 层叠是 CSS 的一个基本特征,定义了如何合并来自多个源的属性值的算法,5 个决定 CSS 样式的源分别是:用户代理样式、页面作者样式、用户样式、动画、过渡;
- 只有在层叠顺序相等时,元素的最终样式使用哪个值才取决于样式的优先级;
- 最新规范中给出的层叠顺序优先级与实际测得的有出入,不同内核浏览器实际表现不一致。
继承:
大部分具有继承特性的属性跟文本有关,还有少部分列表、表格属性。
可以使用inherit关键字显式指定一个属性从其父元素继承。
CSS的值和单位:
盒子模型:
视觉可视化模型:视觉格式化模型 - CSS:层叠样式表 | MDN (mozilla.org)
例子:
- 实现三角形:
//html
<div class="triangle-bottom"></div>
//css
.triangle-bottom{
width:0;
height:0;
border-left:50px solid transparent;
border-right:50px solid transparent;
border-bottom:50px solid red;
}
- 实现固定比例矩形:
//html
<div class="ratio-box"></div>
//css
.ratio-box{
background-color:cadetblue;
//width是父元素宽度100%
width:100%;
height:0;
padding:0;
//padding-bottom是父元素宽度75%
padding-bottom:75%;
}
- 实现水平居中:
//html
<div class="wrap">
<div class="h-center"></div>
</div>
//css
.warp{
width:100%;
height:80px;
border:1px dashed grey;
}
.h-center{
width:100px;
height:50px;
background-color:navajowhite;
//左右两侧的margin 使用auto
//将水平方向剩余空间均分auto
margin:10px auto;
}
- 实现渐变边框
//html
<div class="awesome-border"></div>
//css
.awesome-border{
width:150px;
height:100px;
border:8px solid transparent;
border-radius:12px;
background-clip:padding-box,border-box;
background-origin:padding-box,border-box;
background-image:
linear-gradient(to right,#fff,#fff),
linear-gradient(135deg,#e941ab,#a557ef);
}
负外边距:
margin负值最终减少的是外界可感知的宽高。
布局:
常规流、弹性盒子、Grid、定位
概述:
-
CSS 3之前的常用布局:
- Normal Flow常规流:默认的布局方式,有块级格式化上下文和内联格式化上下文。
- Float 浮动流:用Float属性控制,脱流,做横向布局。
- Positioning 定位流:用position属性控制,fixed 和 absolute 脱离文档流可以自由定位、覆盖等。
-
CSS 3 之后的新增布局
- Flex弹性盒子布局
- Grid 网格布局
- Multicol多列布局
- 一维空间布局
- 二维空间布局
- 文本、内容的多列展示
常规流布局:
块级格式化上下文:
布局规范:在一个块格式区域中,盒子会从包含块的顶部开始,按序垂直排列。同级盒子间的垂直距离会由“margin”属性决定。相邻两个块级盒子之间的垂直间距会遵循外边距折叠原则被折叠。在一个块格式区域中,每个盒子的左外边缘会与包含块左边缘重合 (如果是从右到左的排版顺序则盒子的右外边缘与包含块右边缘重合)。
BFC: BFC本身是block formatting context的缩写,是一种格式规范。如果说一个盒子是BFC或者有BFC特性,那么BFC表示的是block formatting context root。不管它外部显示类型是什么,但是它的内部显示类型是flow-root,其实就是这个盒子内部形成了一个新的块级格式化上下文。
如何触发一个盒子的BFC特性?以下条件满足任何一个即可
- display:flow-root | inline-block;
- position:absolute | fixed;
- float: 不为none;
- overflow: 不为 visible;
外边距塌陷:
内联级格式化上下文:
在内联格式区域中,盒子会从包含块的顶部开始,按序水平排列。只有水平外边距、边框和内边距会被保留。这些盒子可以以不同的方式在垂直方向上对齐:可以底部对齐或顶部对其,或者按文字底部进行对齐。我们把包含一串盒子的矩形区域称为一个线条框。
例子:
- 单行文字垂直居中,比如标题,利用line-height 的垂直居中特性
//html
<div class="title">我是标题</div>
//css
.title{
background:antiquewhite;
font-size:18px;
line-height:36px;
height:36px;//可以省略
}
- 文字和 icon 垂直对齐,利用了line box 中计算高度的原理和vertical-align的设置,垂直对其,但不是完全垂直居中,如果设置父元素font-size:0,基线和中线重叠,则剧中对齐。
<div class="wrap">
<img class="image" src="dy.png">
<span class="text">抖音同款</span>
</div>
//css
.image{
width:240px;
height:24px;
vertical-align:middle;
}
.text{
font-size:16px;
line-height:32px;
margin-left:4px;
verical-align:middle;
}
弹性盒子布局(Flex):
Flexible Box Layout 是为了提供更加高效灵活的布局方式,在即便是宽高位置的情况下,也能排列和分割一个盒子内部的布局。而且在不同的布局方向(横向/纵向)的调整更为灵活。
相关属性:
作用于父元素(flex container):
建立一个弹性盒子
display: flex | inline-flex;
规定盒子的主轴方向
flex-direction: row | column |...;
子元素折行显示形式
flex-wrap:nowrap | wrap | wrap-reverse;
主轴方向子元素的排列方式
justify-content: center | space-between | ...;
交叉轴方向子元素的对齐方式
align-items: flex-start | center | stretch | ...;
交叉轴方向多行子元素的布局方式
align-content: flex-start | space-between I ...;
以明确值设定子元素间的间隔
gap:<row-gap> <column-gap>;
作用于子元素(flex items):
规定item未放缩之前的默认大小
flex-basis: autol长度值...;
规定有剩余空间时,对item的分配比例
flex-grow: number;
规定空间不够时,对item的压缩的比例
flex-shrink:number;
以上三项的缩写
flex:grow shrink basis;默认 0 1 auto
规定item从左到右(row布局)显示的顺序
order:number;默认是0
规定单个item在交叉轴上的位置
align-self:auto | center | flex-start |...
例子:
- 展示灵活的文件长度省略展示:
//html
<div class="filename">
<span class="filename__base">this-file-has-a-really-really-really-long-filename.</span>
<span class="filename__extension">pdf</span>
</div>
//css
.filename{
display:flex;
}
.filename__base {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.filename__extension {
flex-shrink: 0;
}
-
flex 骰子布局:
网格布局(Grid):
2017年退出的 Grid 布局可以定义由行和列组成的二维布局,然后将元素放置到网格中。元素可以只占其中一个单元格,也可以占据多行或多列。
相关属性:
作用于父元素:
作用于父元素
display: grid I inline-grid;
用来生成显式的列、行
grid-template-colums/rows: 数值/百分比/fr/repeat() 等
规定区域(area)的分布
grid-tempate-areas:"header header""sidebar content""footer footer"
规定默认的排序方向
grid-auto-flow: row (dense)l column (dense);
.其他属性
作用于子元素:
规定占用的列、行
grid-colum/row: <start-line> / <end-line>,
是 grid-colum/row-start 和 grid-colum/row-end 的缩写形式
规定占用的area
grid-area: header;
实例:
Grid瀑布流:
定位(position):
绝对定位(absolute):
绝对定位指的是通过规定HTML元素在水平和垂直方向上的位置来固定元素,基于绝对定位的元素不会占据空间。
绝对定位的位置声明是相对于已定位的并且包含关系最近的祖先元素。如果当前需要被定为的元素没有已定位的祖先元素作为参考值,则相对于整个网页。
相对定位(relative):
相对定位与绝对定位的区别在于它的参照点不是左上角的原点,而是该元素本身原先的起点位置。并且即使该元素偏移到了新的位置,也仍然从原始的起点处占据空间。
固定定位(fixed):
固定定位永远都会相对于浏览器窗口进行定位,固定定位会固定在浏览器的某个位置,不会随滚动条滚动。最常用的就是电脑里面是不是弹出的小广告,如果你不差掉它,当时滑动鼠标查看网页时,小广告一直会在那里,还有常用的就是网站或者APP的导航栏和底部的选择栏。
层叠上下文:
层叠上下文是对HTML元素的三维构想,将元素沿着垂直屏幕的虚构的Z轴排开。
形成新的层叠上下文的条件(任一即可):
position:relative 或 absolute;并且 z-index 不是auto
position: fixed 或 sticky
flex 或grid 的子元素;并且z-index 不是auto
opacity 的值小于 1
transform的值不为 none
will-change 的值不为 通用值
......详见规范
层叠顺序:
层叠顺序不仅是指不同的层叠上下文的顺序,同一层叠上下文内,元素间也有顺序:
编写z-index的建议:
使用css变量 或者 预处理语言的变量,管理z-index的值
-
将预设间隔设置10到100,方便后续的插入
--z-loading-indicator:100; --z-nav-menu:200; --z-dropdown-menu:300; --z-modal-backdrop:400; --z-modal-body:410;
CSS动画
变形,过渡,动画
transform 变形:
2D 相关属性:
transform: translate(移动)、rotate(旋转)scale(放缩)、matrix(变形矩阵) 等
transform-origin: right top、center等表示变形时依据的原点
3D相关属性:
transform: translate3d 、rotate3d.scale3d、matrix3d等
transform-origin: right top、50px 30px 等表示变形时依据的原点
transform-style: flat 或 preserve-3d 看子元素的3d表现
perspective:观看点距离z=0这个平面的距离可以在transform中用perspective()使用作用为当前元素也可以直接使用,给后代元素一个统一值
perspective-origin:观看者的位置,如 top、 bottom等
backface-visibility: 元素正面只有朝向观察者的时候可见
transition过渡:
通过指定某些元素属性从一种起始状态,在一段时间内以某种变化节奏,过渡到终止状态。
div{
transition:<property> <duration> <timing-function> <delay>;
}
其中timing-function 一般有三种用法:线性(linear)、贝塞尔曲线(cubic-bezier() 或 ease-in 等)、阶跃(step)
animation动画:
- @keyframes 关键帧 用来定义动画过程中出现的关键样式
-
animation-* 相关属性 用来为元素添加动画
animation-name: 定义好的关键顿的名字 animation-duration: 动画时长 animation-timing-function: 动画节奏 animation-delay:延时开始的时间 animation-iteration-count: 执行次数 animation-direction: 是否反向或交替 animation-fill-mode: 动画执行前后的样式采用 animation-play-state: 动画运行状态
性能相关:
布局:计算布局宽、高、位置等改变都会重新布局(rellow)
绘制:填充元素文本、着色、背景边框等改变会触发(rellow);某些渲染层形成合成层,拥有单独的图层(GraphicLayer)由GPU绘制,即所谓硬件加速;
合成:收集所有绘制完成的图层,按照顺序合成显示。
媒体查询:
媒体查询允许某些样式只在页面满足特定条件时才生效。我们可以将媒体类型(如 screen,print)以及媒体特性(如视口宽度、屏幕比例、设备方向:横向或纵向)做为约束条件。
//html
<link rel="stylesheet" media="screen and (max-width:600px)" href="style.css">
//css中使用格式:@media 媒体类型(省略表示all)逻辑操作符 媒体特性1 逻辑操作符 媒体特性2 ...
@media screen and (min-width:320px) and (orientation:landscape){
...
}
使用媒体查询注意:
- 媒体查询同样遵循cascading 层叠覆盖原则,min- 和 max- 选择一个
- 由于设备的多样化逐渐不可枚举,断点的选择应尽量根据内容选择
- 由于断点的增加会增加样式处理的复杂度,所以尽量减少断点
设备像素:
设备像素(物理像素):显示器上绘制的最小单位,显示屏通过控制每个像素点的颜色,是屏幕显示出不同的图像。
设备像素和设备相关,屏幕从工厂出来那天其,它上面的物理像素点就固定不变了。
DPI && PPI
dpi(dots per inch):每英寸多少点。
ppi(pixels per inch):每英寸多少像素数。
当用于描述显示器设备时 ppi 与 dpi 是同一个概念,说的是每英寸多少物理像素及显示器设备的点距。
当屏幕分辨率是X*Y,计算公式:
CSS像素:
CSS像素(reference pixel)其实是一个视角单位。规范给出的定义是,1css像素是从一臂之遥看解析度为96DPI(即1英寸96点)的设备输出时,1点(即1/96英寸)的视角。
通常认为常人臂长为28英寸,那么视线与水平的夹角是:
但是真正使用的CSS像素并不是用0.0213度去精确计算的,因为css像素存在的目的是为了保证阅读体验一致,所以对不同的物理设备,CSS使得浏览器中的1css像素的大小在不同的物理设备上看上去大小总是差不多。
真正实现时,为了方便基本都是根据设备像素换算的。浏览器根据硬件设备能够直接获取css像素。
DPR设备像素比
设备像素比(dpr):
描述的是为缩放状态下,设备像素和CSS像素的初始比例关系
移动端的viewport
布局视口(viewport)是页面中html元素(根元素)的包含块,默认情况下,window.document.documentElement.clientWidth就是viewport的宽度。
在移动设备中,默认的布局视口由于历史兼容pc屏幕的原因,并不符合需求,我们经常需要用标签对viewport进行设定,来完成移动端设备的适配。
viewport的meta标签的属性
- width
- height
- initial-scale
- minimum-scale
- maximun-scale
- user-scalable
常见的移动端viewport的设置:
-
保持scale为1
<meta name="viewport" content="width=device-width,initial-scale=1"好处:在所有设备上不管横屏还是竖屏,让布局视口的宽度和设备屏幕的宽度保持一致, 且参考像素不放缩。
坏处:如果设备的dpr>1,那么想要画出一个设备像素粗细的线,需要其他方法实现。
-
保持scale放缩参数是1/dpr
const calcScale=1 / window.devicePixelRation; <!-- 在meta标签中使用calcScale的值,示意如下 --> <meta neme="viewport" content="initial-scale=calcScale,maximum-scake=cakcScake,minimum-scale=calcScale,user-scalable=no">好处:1个css px 等于1个设备像素,全局层面解决”真实1像素“问题。
坏处:不同设备想达到1个css px严格等于1个设备像素,需要处理兼容性才能达到。而且,全局的比缩放,对某些固定尺寸需要特殊处理。
em:
- 在非font-size属性中使用是相对于自身的字体大小。
- 在font-size上使用是相对于父元素的字体大小(一般不在这个属性上使用,因为font-size的继承性特性,多重嵌套后大小会不断放大或缩小产生混乱)。
rem:
根元素的字体大小。通过伪类:root或者html选择器选定。由于是根元素的font-size,所以不会像em那样出现多重嵌套问题,减少了复杂性,同时作为一个相对单位,可以进行适配放缩,可以用来做响应式布局。
vw和vh:
vw: 视窗宽度的1%.
vh: 视窗宽度的1%
同样,vw 也可以作为响应式布局的基准单位。由于vw是天然是视口宽度的1%,所以不需要js动态配置。
语言增强
CSS预处理器
scss、less、stylus
scss、less、stylus简单对比:
| scss | less | stylus | |
|---|---|---|---|
| css语法兼容 | 兼容 | 兼容 | 常规兼容 |
| 可编程能力 | 较强,逻辑处理能力丰富 | 较弱,不支持自定义函数 | 较强 |
| 社区活跃、使用人数 | 最多 | 较多 | 相对少 |
CSS后处理器:
postcss:
工程架构
CSS模块化:
CSS Module 就是为了解决全局污染问题出现的方案,解决CSS全局污染,本质上是保证样式集合对应的选择器是唯一的,从这个角度看,主流的单纯针对于防止全局污染的方案大概有:
CSS-in-JS
CSS-in-js:将应用的CSS样式卸载Javascript文件里面,利用js动态生成css
- inline-style ——代表:radium
- unique classname ——代表:style-component
style-component机制:
- 生成第一个classname作为componentld,这个类名没有CSS样式,但是当需要引用其他组件的时候,可以作为一个嵌套选择器来使用
- 生成第二个classname作为唯一类名(hash值),使用stylis,生成和唯一类名关联的CSS字符串
- 唯一类名对应的css样式insert到的中
- 将两个类名写到目标节点的class中
pros & cons
原子化CSS
原子化CSS是一种CSS的架构方式,它倾向于小巧且用途单一的class,并以视觉效果进行命名。
Tailwind设计:
-
实用工具库优先(Utilities-First)
利用基础的规则库预先定义好生成的规则
@tailwind base; @tailwind components; @tailwind utilities; -
按需生成
<body> <h1 class="text-3xl font-bold underline text-blue flex-grow"> Hello World! </h1> </body> <style> .flex-grow{ flex-frow:1; } .text-3xl{ font-size:1.875rem; line-height:2.25rem; } .font-bold{ font-weight:700; } .underline{ text-decoration-line:underline; } </style>
- 支持配置样式规则&自定义插件。