《深入解析CSS》笔记

层叠、优先级和继承

层叠

规则考虑顺序

  1. 样式表来源

    • !important(使用了!important的声明会被当作更高优先级的来源对待)
    • 作者样式表(前端开发样式)
    • 用户样式表(游览器插件样式)
    • 用户代理样式(游览器默认样式)
  2. 优先级

    • 行内样式(可以理解为带作用域的样式声明)
    • 选择器
      • id -> class -> tag
      • 通用选择器(*)和组合器(>、+、~)对优先级没有影响
  3. 源码顺序

规则作用结果

优先级选择器的数量决定了最终样式作用结果

  • 如果选择器的id数量更多,则它会胜出。
  • 如果id数量一致,那么拥有最多class选择器的胜出。
  • 如果以上两次比较都一致,那么拥有最多tag选择器的胜出。

优先级标记

常用的表示优先级的方式是用数值形式来标记

  • 比如1,2,2,表示选择器由1个id、2个class、2个tag组成。
  • 优先级最高的id列为第一位,紧接着是class,最后是tag。
  • 如果考虑内联样式,则数值标记可以用4位表示,0或1代表内联数量。
选 择 器 ID 标签 标记
html body header h1 0 0 4 0, 0, 4
body header.page-header h1 0 1 3 0, 1, 3
.page-header .title 0 2 0 0, 2, 0
#page-title 1 0 0 1, 0, 0

两条经验法则

  1. 在选择器中不要使用ID
  2. 不要使用 !important

继承

  • 如果一个元素的某个属性没有层叠值,则可能会继承某个祖先元素的值。
  • 不是所有的属性都能被继承,主要是跟文本和列表属性相关的属性。
  • 继承属性会顺序传递给后代元素,直到它被层叠值覆盖。

特殊值

  • inherit(继承父元素的属性值)——有时,我们想用继承代替一个层叠值,这时可以用inherit关键字
  • initial(重置为属性的默认值)——有时,我们需要撤销作用于某个元素的样式,这时可以用initial关键字

简写属性

简写属性会默默覆盖其他样式

  • 大多数简写属性可以省略一些值,只指定我们关注的值,但是这样做仍然会设置省略的值,它们会被隐式地设置为初始值

简写值的顺序

  • “上、下、左、右”口诀适用于分别给盒子设置四个方向的值的属性,对应的有:margin、padding
  • “水平、垂直”口诀适用于从一个点出发的两个方向的值的属性,对应的有:background-position、box-shadow、text-shadow

相对单位

em和rem

使用em定义字号

  • 使用em定义自身font-size时,其值是根据继承的font-size字号来计算的
  • 使用em定义其他属性时,会参照本身font-size的计算值来计算

使用rem设置字号

  • html根节点有一个伪类选择器:root,优先级相当于类名
  • 拿不准的时候,用rem设置字号,用px设置边框,用em设置其他大部分属性

css变量

css变量能够层叠和继承

  • 层叠——子级定义的相同变量会覆盖父级定义的变量,类似于“作用域变量”
  • 继承——子级会继承父级定义的变量,任何之下的子级都可以使用

使用javascript改变自定义属性

  • 访问javascript自定义属性

    var rootElement = document.documentElement; // 获取根元素
    var styles = getComputedStyle(rootElement); // 获取层叠后的样式
    var mainColor = styles.getPropertyValue('--main-bg'); // 获取定义的css变量
    console.log(String(mainColor).trim()); // 确保mainColor是一个字符串,并去掉前后空格
    
  • 使用javascript设置自定义属性

    var rootElement = document.documentElement; // 获取根元素
    rootElement.style.setProperty('--main-bg', '#cdf'); // 设置css变量
    

盒模型

元素宽度的问题

在设置一个元素的宽/高时,盒模型默认指定的只是内容的宽/高,因此所有内边距、边框、外边距都是追加到该宽/高值上的

解决办法

  • calc(宽度值 - 内边距/外框/外边距)
  • 调整盒模型box-sizing: border-box

最佳实践

:root {
  box-sizing: border-box;
}

*,
::before,
::after {
  box-sizing: inherit;
}

// 涉及到组件库影响的问题时
.component-lib-container {
  box-sizing: content-box;
}

元素高度的问题

  • 元素宽度的问题的border-box的修改依然适用于高度。
  • 普通文档流是为限定的宽度和无限的高度设计的,容器的高度由内容天然地决定,而不是容器自己决定。

控制溢出行为

  • 当明确设置一个元素的高度时,内容可能会溢出容器。
  • 解决办法:overflow

百分比高度的备选方案

  • 用百分比指定高度会存在问题,百分比参考的是元素容器块的大小,但是容器的高度通常是由子元素的高度决定的。这样会造成死循环,浏览器处理不了,因此它会忽略这个声明。

  • 要想让百分比高度生效,必须给父元素明确定义一个高度。

  • 我们使用百分比高度通常是想让一个容器填满屏幕,不过更好的方式是用视口的相对单位vh,100vh等于视口的高度,还有一个更常见的用法是创造等高列。

  • 创造等高列的2种方法:display: table和Flexbox

垂直居中指南

  • 可以用一个自然高度的容器吗?——可以采用给容器加上相等的上下内边距让内容居中。

  • 容器需要指定高度或者避免使用内边距吗?——对容器使用 display: table-cell和vertical-align: middle。

  • 可以用Flexbox吗?——如果不需要支持IE9,可以用Flexbox居中内容。

  • 容器里面的内容只有一行文字吗?设置一个大的行高,让它等于理想的容器高度。这样会让容器高度扩展到能够容纳行高。如果内容不是行内元素,可以设置为inline-block。

  • 容器和内容的高度都知道吗?将内容绝对定位。(只有当前面提到的方法都无效时才推荐这种方式。)

  • 不知道内部元素的高度?用绝对定位结合变形。(只有当前面提到的方法都无效时才推荐这种方式。)

负外边距

不同于内边距和边框宽度,外边距可以设置为负值,负外边距的具体行为取决于设置在元素的哪边。

如果不给一个块级元素指定宽度,它会自然地填充容器的宽度。但如果在右边加上负外边距,则会把它拉出容器。如果在左边再加上相等的负外边距,元素的两边都会扩展到容器外面。

外边距折叠

  • 只有上下外边距会产生折叠,左右外边距不会折叠。

  • 折叠边距的值会取所有边距中的最大值。

原因

主要原因与包含文字的块之间的间隔相关。

类型

相邻元素折叠

  • 同一个容器内,两个上下相邻的元素如果都设置有外边距,会存在折叠。

  • 即使两个元素的外边距不是直接来自于自身,而是来自于内部子元素,这两个元素还是会发生折叠。

  • 如果在页面中添加一个空的、无样式的div(没有高度、边框和内边距),它自己的顶部和底部外边距就会折叠。

    <main class="main">
      <h2>123</h2>
      <!--就算用另一个div包裹,段落的外边距还是会折叠-->
      <div>
        <p>456</p>
      </div>
    </main>
    

元素单边折叠

如果容器外部没有相邻元素,或者其相邻元素没有设置外边距,容器本身的边距和内部子元素的边距会存在折叠现象。

解决办法

  • 针对相邻元素折叠

    • 可以通过设置内边距或者边框避免
    • 对容器设置display: flex,flex-direction: column。
  • 针对元素单边折叠

    • 对容器使用overflow: auto,或者非visible的值,这种方式副作用最小。
    • 对容器设置浮动、定位或内联。
    • 对容器设置display: flex。

容器内的元素间距

针对容器有固定内边距,但是连续的内容元素(如:li)之间又需要边距时,可以采用猫头鹰布局(+),代码示例如下:

.box * + * {
  margin-top: 1.5em;
}

采用猫头鹰布局可以保证只有除首尾外的连续元素之间存在间距,这样就不会和容器的内边距叠加,造成界面不对称的美观问题。

理解浮动

浮动设计的初衷

浮动元素会被移出正常文档流,并被拉到容器边缘。文档流会重新排列,但是它会包围浮动元素此刻所占据的空间。浮动的初衷,就是为了实现文字围绕浮动元素排列的效果。

如果让多个元素向同侧浮动,它们就会挨着排列。

要实现将图片移动到网页一侧,并且让文字围绕图片的效果,浮动仍然是唯一的方法。

如果在段落里浮动图片,段落的高度并不会增长到能够容纳该图片。也就是说,如果图片比段落文字高,下一段会直接从
上一段的文字下面开始,两段文字都会围绕浮动的图片排列。

浮动陷阱

解决办法

清除每行的第一个元素上面的浮动,可以用:nth-child()伪类选择器命中目标元素。

// 使用:nth-child()选择器选取第奇数个元素
li:nth-child(odd) {
  clear: left;
}

容器折叠和清除浮动

理解容器折叠

蓝色区域为header头部,底部为main包裹区域,内部分为title和box,main本身设置了白色背景,四个媒体盒子宽度等分,如果将这四个盒子设置浮动到左侧,我们能看到容器折叠的问题,如下图所示:

白色背景延伸到title就结束了,这是怎么回事?因为浮动元素不同于普通文档流的元素,它们的高度不会加到父元素上。这可能看起来
很奇怪,但是恰好体现了浮动的设计初衷。

解决办法:在容器内容尾部插入一个div并对它使用清除浮动clear: both,因为空div本身没有浮动,所以容器就会扩展,直到包含它,因此也会包含该div上面的浮动元素。

理解清除浮动

  • 浮动元素的外边距不会折叠到清除浮动容器的外部,非浮动元素的外边距则会正常折叠。

  • 外边距无法通过表格单元格元素折叠,表格单元格内设置外边距本身也无效。

清除浮动-版本1

// 该版本不能解决顶部非浮动元素设置外边距造成的折叠问题
.clearfix::after {
  display: block;
  content: " ";
  clear: both;
}

清除浮动-版本2

// 该版本能解决顶部非浮动元素设置外边距造成的折叠问题
.clearfix::before,
.clearfix::after {
  display: table; // 通过display: table,充分利用了外边距无法通过表格单元格元素折叠的特性
  content: " ";
}
.clearfix::after {
  clear: both;
}

BFC

全称块级格式化上下文。它是网页的一块区域,元素基于这块区域布局,虽然BFC本身是环绕文档流的一部分,但它将内部的内容与外部的上下文隔离开。这种隔离为创建BFC的元素做出了以下3件事情:

  • 包含浮动元素

  • 防止外边距折叠

  • 防止文档流围绕浮动元素排列

简而言之,BFC里的内容不会跟外部的元素重叠或者相互影响。如果强制给一个元素生成一个新的BFC,它不会跟其他BFC重叠。

典型场景示例:

在一个容器中,左侧是图片,应用了浮动,右边是一个div包含了上下文字,右边底部的文字因为内容太多,加上浮动的影响,形成的环绕效果,但是我们其实需要的是左右齐分的效果。如果给右侧div添加浮动,它会摆脱浮动影响,但是也会因为div默认100%而自动换行,所以这个时候我们就可以为右侧div创建一个BFC来解决问题。

给元素添加以下的任意属性值都会创建BFC:

  • float:left或right,不为none即可。
  • overflow:hidden、auto或scroll,不为visible即可。
  • position:absolute或fixed。
  • display:inline-block、table-cell、table-caption、flex、inline-flex、grid或inline-grid。拥有这些属性的元素称为块级容器(block container)。

需要强调的是:

  • 使用浮动或者inline-block方式创建BFC的元素宽度会变成100%,因此需要限制一下元素的宽度,防止因为过宽而换行。
  • 相反,使用table-cell方式显示的元素,其宽度只会刚好容纳其中的内容,因此需要设置一个较大的宽度,强制使其填满剩余空间。
  • 使用overflow:auto通常是创建BFC最简单的一种方式,它会刚好填充合适的宽度。
  • 某些情况下,BFC中的内容可能还是会与别的BFC的内容重叠。比如,内容溢出了容器(比如内容太宽)或者因为负外边距导致内容被拉到容器外面。

Flexbox

Flexbox的原则

Flexbox布局是以主轴和副轴为基础来定义的,子元素按照主轴线排列。

主轴的方向为主起点(左)到主终点(右)。

副轴的方向从副起点(上)到副终点(下)。

Flexbox允许使用margin: auto来填充弹性子元素之间的可用空间。

弹性子元素的大小

flex属性

  • flex属性控制弹性子元素在主轴方向上的大小,它是flex-grow、flex-shrink和flex-basis属性的简写,默认值为0 1 auto。

  • 与大部分简写属性不一样,如果在flex中忽略某个子属性,那么子属性的值并不会被置为默认值,而是会设置为更有用的组合值。

  • 当flex属性给定一个无单位数值时,如flex:1,实例组合值为:1 1 0%,而非1 1 auto。

  • 当flex属性给定一个有单位数值时,如flex:30%,实际组合值为:1 1 30%,而非30% 1 auto。

flex-basis属性

  • 定义了元素大小的基准值,即一个初始的主尺寸。

  • 可以设置为任意的宽度值,包括px、em、百分比,它的初始值是auto。

  • flex-basis设置了具体值后,会忽略width属性值。

  • 子元素基准值考虑顺序:是否有flex-basis具体值->是否有width属性值->auto(用元素内容自身的大小)。

flex-grow属性

  • 该属性属于增长因子,可以理解为放大,值为正整数。

  • 如果某个弹性子元素的flex-grow值为0,那么它的宽度不会超过flex-basis基准值。

  • 如果某个弹性子元素的flex-grow值为非0,那么这些元素会增长到所有的容器剩余空间被分配完。

  • flex-grow的值越大,元素的权重越高,也就会占据更大的剩余宽度。

flex-shrink属性

  • 该属性属于收缩因子,可以理解为缩小,值为正整数。

  • 计算出弹性子元素的初始主尺寸后,它们的累加值可能会超出弹性容器的可用宽度,如果不用flex-shrink,就会导致溢出。

  • 每个子元素的flex-shrink值代表了它是否应该收缩以防止溢出。

  • 如果某个弹性子元素为flex-shrink: 0 ,则不会收缩;如果值大于0,则会收缩至不再溢出。

  • 按照flex-shrink值的比例,值越大的元素收缩得越多,在整体比例中占据得也就越小。

实际应用

弹性方向

理对齐、间距等细节

弹性容器的属性

弹性子元素的属性

网格布局

网格剖析

重点概念

  • 网格容器:设置了display:grid的元素被称为网格容器。

  • 网格元素:网格容器的子元素。

  • 网格线:网格线构成了网格的框架。一条网格线可以水平或垂直,也可以位于一行或一列的任意一侧。

  • 网格轨道:一个网格轨道是两条相邻网格线之间的空间。网格有水平轨道(行)和垂直轨道(列)。

  • 网格单元:网格上的单个空间,水平和垂直的网格轨道交叉重叠的部分。

  • 网格区域:网格上的矩形区域,由一个到多个网格单元组成。该区域位于两条垂直网格线和两条水平网格线之间。

网格编号

浏览器给网格里的每个网格线都赋予了编号,CSS用这些编号指出每个元素应该摆放的位置。

属性解析

  • grid-template-columns:定义列划分,可以多个(垂直轨道),数值单位不限(fr、px、em或百分数)

  • grid-template-rows:定义行划分,可以多个(水平轨道),数值单位不限(fr、px、em或百分数)

  • grid-gap:网格间距,分别指定垂直和水平方向的间距

  • grid-column:定义了网格元素列的开始和结束位置,是grid-column-start/grid-column-end属性的简写

  • grid-row:定义了网格元素行的开始和结束位置,是grid-row-start/grid-row-end属性的简写

单位/函数/关键字

  • fr —> 分数单位,用于网格布局,跟Flexbox中flex-grow因子的表现一样

    grid-template-columns: 1fr 1fr 1fr; // 定义等宽的三列
    grid-template-rows: 1fr 1fr; // 定义等高的两行
    
  • repeat() -> 定义重复模式,接受多个参数,第一个参数为重复次数,后面的参数为被重复参数

  • span -> 告诉浏览器元素需要占据(跨越)的网格轨道,可以用于来指定grid-row和grid-column的值

    nav {
      grid-column: 1 / 3;
      grid-row: span 1; // 这里会占据一个网格轨道,因为这里没有指出具体是哪一行,所以会根据网格元素的布局算法自动将其放到合适的位置
    }
    

与Flexbox的区别

  • Flexbox是一维的,所以它很适合用在相似的元素组成的行(或列)上。它支持用flex-wrap换行,但是没法让上一行元素跟下一行元素对齐。
    相反,网格是二维的,旨在解决一个轨道的元素跟另一个轨道的元素对齐的问题。

  • Flexbox是以内容为切入点由内向外工作的,而网格是以布局为切入点从外向内工作的。Flexbox让你在一行或一列中安排一系列元素,但是它们的大小不需要明确指定,每个元素占据的大小根据自身的内容决定。而在网格中,首先要描述布局,然后将元素放在布局结构中去。虽然每个网格元素的内容都能影响其网格轨道的大小,但是这同时也会影响整个轨道的大小,进而影响这个轨道里的其他网格元素的大小。

替代语法

命名的网格线

示例如下:

// 三条垂直的网格线分别叫作start、center和end
grid-template-columns: [start] 2fr [center] 1fr [end];

// 将网格元素放在1号网格线(start)到2号网格线(center)之间的区域
grid-column: start/center;

还可以给同一个网格线提供多个名称,比如下面的声明:

// 2号网格线既叫作left-end也叫作right-start,之后可以任选一个名称使用
grid-template-columns: [left-start] 2fr
                       [left-end right-start] 1fr
                       [right-end];

将网格线命名为left-start和left-end,就定义了一个叫作left的区域,这个区域覆盖两个网格线之间的区域。如果给元素设置grid-column: left ,它就会跨越从left-start到left-end的区域。

//  repeat()里声明了一条命名的水平网格线,于是每条水平网格线被命名为row(除了最后一条)。
grid-template-rows: repeat(4, [row] auto);

// 将main元素放在从row 3(第三个叫row的网格线)开始的地方,并跨越两个网格轨道。
.main {
  grid-column: left;
  grid-row: row 3 / span 2;
}

命名的网格区域

// 通过grid-template-areas属性,将每个网格单元分配到一个命名的网格区域中
.container {
  display: grid;
  grid-template-areas: "title title"
                       "nav nav"
                       "main aside1"
                       "main aside2";
  grid-template-columns: 2fr 1fr;
  grid-template-rows: repeat(4, auto);
  grid-gap: 1.5em;
  max-width: 1080px;
  margin: 0 auto;
}

// 通过grid-area属性,将每个网格元素放到一个命名的网格区域
header {
  grid-area: title;
}
nav {
  grid-area: nav;
}
.main {
  grid-area: main;
}
.sidebar-top {
  grid-area: aside1;
}
.sidebar-bottom {
  grid-area: aside2;
}

每个命名的网格区域必须组成一个矩形,不能创造更复杂的形状,比如L或者U型。

还可以用句点(.)作为名称,这样便能空出一个网格单元。

grid-template-areas: "top top right"
                     "left . right"
                     "left bottom bottom";

显式和隐式网格

使用grid-template-*属性定义网格轨道时,创建的是显式网格。

隐式网格轨道默认大小为auto ,也就是它们会扩展到能容纳网格元素内容。

  • 可以给网格容器设置和grid-auto-rows,为隐式网格轨道指定一个大小,比如:grid-auto-columns: 1fr。

属性解析

  • grid-auto-rows:为隐式网格轨道列指定一个大小。

  • grid-auto-columns:为隐式网格轨道行指定一个大小。

  • grid-template-columns:repeat(auto-fill, minmax(200px, 1fr))。

    • minmax()函数用于指定最小和最大值,浏览器会确保网格轨道的大小介于这两者之间,如果最大尺寸小于最小尺寸,最大尺寸就会被忽略。

    • repeat()函数里的auto-fill关键字是一个特殊值,设置了之后,只要网格放得下,浏览器就会尽可能多地生成轨道,并且不会跟指定大小(minmax() 值)的限制产生冲突。

    • auto-fill和minmax(200px, 1fr)加在一起,就会让网格在可用的空间内尽可能多地产生网格列,并且每个列的宽度不会小于200px。因为所有轨道的大小上限都为1fr(最大值),所以所有的网格轨道都等宽。

    • 如果网格元素不够填满所有网格轨道,auto-fill就会导致一些空的网格轨道。如果不希望出现空的网格轨道,可以使用auto-fit关键字代替 auto-fill。它会让非空的网格轨道扩展,填满可用空间。

    • 具体选择auto-fill还是auto-fit取决于你是想要确保网格轨道的大小,还是希望整个网格容器都被填满。

  • grid-auto-flow:控制布局算法的行为,精确指定在网格中被自动布局的元素怎样排列。

    • 它的初始值是row,如果值为column,它就会将元素优先放在网格列中,只有当一列填满了,才会移动到下一行。

    • 还可以额外应用一个关键字dense,它让算法紧凑地填满网格里的空白,让小元素回填大元素造成的空白区域,尽管这会改变某些网格元素的顺序。

    • 使用了grid-auto-flow: dense,等价于grid-auto-flow: row dense,因为grid-auto-flow属性默认值是row。

定位和层叠上下文

定位

  • 静态定位:如果我们用非static值,我们就说元素就被定位了,反之,元素则未被定位。

  • 固定定位:让元素相对于视口定位,此时视口被称作元素的包含块。

  • 绝对定位:让元素相对于最近的祖先元素定位,如果祖先元素都没有定位,那么绝对定位的元素会基于初始包含块来定位,初始包含块跟视口一样大,固定在网页的顶部。

  • 相对定位:让元素相对于自身位置定位,元素仍然处在文档流中,不影响其他元素的布局。

    • 跟固定或者绝对定位不一样,不能用top、right、bottom和left改变相对定位元素的大小,只能让元素在上、下、左、右方向移动。

    • 可以用top或者bottom ,但它们不能一起用,bottom会被忽略。同理,可以用left或right,但它们也不能一起用,right会被忽略。

  • 粘性定位:相对定位和固定定位的结合体。正常情况下,元素会随着页面滚动,当到达屏幕的特定位置时,如果用户继续滚动,它就会“锁定”在这个位置。

    • 只有当父元素的高度大于粘性元素的高度,并且粘性元素随着父元素一起滚动,滚动到接近屏幕边缘时才会被“锁定”,而且这个“锁定”是有区间范围的,这个范围等于父元素高度减去粘性元素高度。

    • 当父元素随着页面向上滚动,内部的粘性元素到达屏幕顶部后会被“锁定”,但是当父元素随着滚动,其底边到达粘性元素的底边时,粘性元素会失去“锁定”,随着父元素继续滚动,直至消失在屏幕视区(页面可以继续滚动的话)。

层级高低

有如下3个div:

<div>one</div>
<div>two</div>
<div>three</div>

正常的文档流情况下,元素在HTML里出现的顺序决定了游览器绘制的顺序,如果元素刚好重叠,后绘制的元素就会出现在先绘制的元素前面。

定位元素时,这种行为会改变,浏览器会先绘制所有非定位的元素,然后绘制定位元素。默认情况下,所有的定位元素会出现在非定位元素前面。

不过基于源码顺序的层叠关系并没有改变,因此在定位元素里,第二个定位元素还是出现在第一个定位元素前面,当然可以通过z-index属性进行改变,但是拥有负数z-index的元素出现在静态元素后面。

总结来看,关于层叠问题,层级高低取决于:

  1. 设置了定位,z-index属性值为正数

  2. 设置了定位,没设置z-index,源码顺序靠后

  3. 没设置定位,源码顺序靠后

  4. 设置了定位,z-index属性值为负数

层叠上下文

  • 一个层叠上下文包含一个元素或者由浏览器一起绘制的一组元素,其中一个元素会作为层叠上下文的根。

  • 如果给一个定位元素加上z-index就会创建一个层叠上下文,这个元素也就成了一个新的层叠上下文的根。

  • 一个层叠上下文之内的元素独立设置的z-index大小不会改变跟其他外部层叠上下文的层叠高低,只有代表这个层叠上下文根的元素设置z-index才能改变层叠高低。

  • 创建层叠上下文的其他方式还包括:设置小于1的opacity属性,还有transform和filter属性,由于这些属性主要会影响元素及其子元素渲染的方式,因此一起绘制父子元素。

  • 文档根节点html也会给整个页面创建一个顶级的层叠上下文。

  • 所有层叠上下文内的元素会按照以下顺序,从后到前叠放:

    • 层叠上下文的根

    • z-index为负的定位元素(及其子元素)

    • 非定位元素

    • z-index为auto的定位元素(及其子元素)

    • z-index为正的定位元素(及其子元素)

模块化CSS

模块化CSS是指把页面分割成不同的组成部分,这些组成部分可以在多种上下文中重复使用,并且互相之间没有依赖关系。最终目的是,当我们修改其中一部分CSS时,不会对其他部分产生意料之外的影响。

模块的变体

有时候我们需要区分一个模块的状态,可以通过定义修饰符来实现,也就是使用两个连字符来表示修饰符,这种写法出自BEM的CSS命名规范。

.message {}
.message--success {}
.message--warning {}
.message--error {}

强调:千万不要使用基于页面位置的后代选择器来修改模块。坚决遵守这个原则,就可以有效防止样式表变成一堆难以维护的代码。

带子元素模块

大多数情况下我们需要处理多层级元素,这个时候可以使用双下划线修饰符来定义元素类名,这样的类名可以清楚地告诉我们这个元素扮演了什么角色、属于哪个模块,这是 BEM命名规范里的另一种约定。

.media {}
.media__image {}
.media__body {}

强调:应该避免在模块选择器中使用通用标签名,因为其命中的范围太宽泛了,谁也说不准以后会不会出于其他目的再添加第二个相同的通用标签,当我们想为其追加类名时,可能会出现多处模块查找,全部改一遍的问题。

模块命名

  • 模块的名称应该简单易记,能适用于各种不同场景,而不局限于任何特定用法或者视觉描述。

  • 为模块的变体类命名的时候,也遵守同样的原则,应该避免使用精确描述的修饰符,因为不确定后面是否会改变,比如:

    • button--red

    • button--20px

工具类

  • 工具类应该专注于某种功能,一般只声明一次。

  • 工具类应该是唯一应该使用important注释的地方。

  • 建议放在样式表的底部,模块代码的下面。

总结

  • 把CSS拆解成可复用的模块。

  • 不要书写可能影响其他模块或者改变其他模块外观的样式。

  • 使用变体类,提供同一模块的不同版本。

  • 把较大的结构拆解成较小的模块,然后把多个模块组合在一起构建页面。

  • 在样式表中,把所有用于同一个模块的样式放在一起。

  • 使用一种命名约定,比如双连字符和双下划线,以便一眼就可以看清楚模块的结构

posted @ 2022-06-17 09:30  戡玉  阅读(245)  评论(0)    收藏  举报