乐趣区

关于html5:8个硬核技巧带你迅速提升CSS技术-掘金直播总结

前言

前段时间笔者收到 可恶丑陋的小册姐姐 的邀请,做了人生首次直播分享。分享主题是《玩转 CSS 的艺术之美》,跟笔者在 9 月底公布的掘金小册同名。

9 月底公布的玩转 CSS 的艺术之美,首日预售就达到709 本,预售仅三日就破1000 本。这也让笔者感到诧异,没想到 CSS 技术还是那么受倔友们的欢送,让笔者感觉熬夜半年写这本小册还是值得的,毕竟能将本人的学习心路分享进来,让更多同学学到更多货色,也是一件值得开心的事件。

因为首次做直播分享,感觉比拟缓和,家里网络不是特地好,还有其余起因,导致认真筹备的内容未在意料工夫内实现分享,因而通过本文将来不及分享的内容整理出来。

目录

对分享内容感兴趣的同学可关注笔者的公众号 IQ 前端,回复 CSSPPT 下载分享 PPT。分享内容蕴含 历史背景 概念原理 开发技巧 三节。第一二节比拟无聊,可自行查看 PPT,在此就不多说了。次要是第三节的干货,是笔者认真筹备了好几天的内容,每个主题都会有对应的源码及其成果。

笔者抉择了一些罕用甚至有些小册都未提及到的干货作为分享内容,置信这些内容能帮忙同学们在短期内晋升 CSS 编码素质,实现一些看似只能由 JS 能力实现的成果。

  • 神奇的选择器
  • 浅谈布局那些事
  • 绘制三角的原理
  • 完满极致的变量
  • 添枝加叶的伪元素
  • 灵便多变的障眼法
  • 动向不到的内容插入
  • 无所不能的模仿点击事件

筹备工作

整个分享过程不搞那些乌七八糟的环境搭建。既然只玩 CSS,那只有 html 文件css 文件 就足够了。另外还需一个浏览器 Chrome 和一个编辑器VSCode

VSCode还需装置 Live Sass CompilerLive Server两个插件。Live Sass Compiler用于实时编译 sass/scss 文件css 文件 Live Server 用于启动具备实时刷新性能的本地开发服务器,以解决动态页面和动静页面。

新建 index.htmlindex.scss。为了使各大浏览器默认款式统一,还需引入一个磨平浏览器默认款式的css 文件,同学们可下载笔者写好的 reset.css 到本地目录里。

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, minimum-scale=1, maximum-scale=1">
    <title>Hello CSS</title>
    <link rel="stylesheet" href="./reset.css">
    <link rel="stylesheet" href="./index.css">
</head>

<body class="flex-ct-x">
    <!-- ... -->
</body>

</html>
body {
    overflow: hidden;
    height: 100vh;
}

上述文件骨架实现后,关上 index.scss,按F1Cmd + Shift + P关上命令面板,输出 Watch Sass 监听 index.scss 并生成 index.css,再输出Open With Live Server 启动本地开发服务器并关上浏览器。到此为止就实现了所有筹备工作了。

神奇的选择器

很多 CSS 编码习惯都是清一色的类而无相应的选择器,层层嵌套的标签都蕴含至多一个类。选择器 比照起来性能上的确没后者那么好,但现在浏览器对于 CSS 的解析速度已失去大大的晋升,齐全可疏忽选择器那丁点的性能问题。

可是 CSS 模块泛滥,顺次推出的选择器也很多。若无特地办法记熟这些选择器对应的性能,也很难将选择器施展到最大作用。玩转 CSS 的最要害一步是 能相熟大部分选择器及其性能与应用场景

笔者依据选择器的性能划分出八大类,每个类别的选择器都能在一个应用场景中相互组合,记熟这些类别的选择器,置信就能将选择器施展到最大作用。以下选择器的 罕用选项 里若打勾可强行记熟,这些选择器都是笔者久经沙场而标记进去且认为是最好用的选择器。

根底选择器

选择器 别名 阐明 版本 罕用
tag 标签选择器 指定类型的 标签 1
#id ID 选择器 指定身份的 标签 1
.class 类选择器 指定类名的 标签 1
* 通配选择器 所有类型的 标签 2

档次选择器

选择器 别名 阐明 版本 罕用
elemP elemC 后辈选择器 元素的 后辈元素 1
elemP>elemC 子代选择器 元素的 子代元素 2
elem1+elem2 相邻同胞选择器 元素相邻的 同胞元素 2
elem1~elem2 通用同胞选择器 元素前面的 同胞元素 3

汇合选择器

选择器 别名 阐明 版本 罕用
elem1,elem2 并集选择器 多个指定的 元素 1
elem.class 交加选择器 指定类名的 元素 1

条件选择器

选择器 阐明 版本 罕用
:lang 指定标记语言的 元素 2 ×
:dir() 指定编写方向的 元素 4 ×
:has 蕴含指定元素的 元素 4 ×
:is 指定条件的 元素 4 ×
:not 非指定条件的 元素 4
:where 指定条件的 元素 4 ×
:scope 指定 元素 作为参考点 4 ×
:any-link 所有蕴含 href链接元素 4 ×
:local-link 所有蕴含 href 且属于相对地址的 链接元素 4 ×

状态选择器

选择器 阐明 版本 罕用
:active 鼠标激活的 元素 1 ×
:hover 鼠标悬浮的 元素 1
:link 未拜访的 链接元素 1 ×
:visited 已拜访的 链接元素 1 ×
:target 以后锚点的 元素 3 ×
:focus 输出聚焦的 表单元素 2
:required 输出必填的 表单元素 3
:valid 输出非法的 表单元素 3
:invalid 输出非法的 表单元素 3
:in-range 输出范畴以内的 表单元素 3 ×
:out-of-range 输出范畴以外的 表单元素 3 ×
:checked 选项选中的 表单元素 3
:optional 选项可选的 表单元素 3 ×
:enabled 事件启用的 表单元素 3 ×
:disabled 事件禁用的 表单元素 3
:read-only 只读的 表单元素 3 ×
:read-write 可读可写的 表单元素 3 ×
:target-within 外部锚点元素处于激活状态的 元素 4 ×
:focus-within 外部表单元素处于聚焦状态的 元素 4
:focus-visible 输出聚焦的 表单元素 4 ×
:blank 输出为空的 表单元素 4 ×
:user-invalid 输出非法的 表单元素 4 ×
:indeterminate 选项未定的 表单元素 4 ×
:placeholder-shown 占位显示的 表单元素 4
:current() 浏览中的 元素 4 ×
:past() 已浏览的 元素 4 ×
:future() 未浏览的 元素 4 ×
:playing 开始播放的 媒体元素 4 ×
:paused 暂停播放的 媒体元素 4 ×

构造选择器

选择器 阐明 版本 罕用
:root 文档的 根元素 3 ×
:empty 无子元素的 元素 3
:nth-child(n) 元素中指定顺序索引的 元素 3
:nth-last-child(n) 元素中指定逆序索引的 元素 3 ×
:first-child 元素中为首的 元素 2
:last-child 元素中为尾的 元素 3
:only-child 父元素仅有该元素的 元素 3
:nth-of-type(n) 标签中指定顺序索引的 标签 3
:nth-last-of-type(n) 标签中指定逆序索引的 标签 3 ×
:first-of-type 标签中为首的 标签 3
:last-of-type 标签中为尾 标签 3
:only-of-type 父元素仅有该标签的 标签 3

属性选择器

选择器 阐明 版本 罕用
[attr] 指定属性的 元素 2
[attr=val] 属性等于指定值的 元素 2
[attr*=val] 属性蕴含指定值的 元素 3
[attr^=val] 属性以指定值结尾的 元素 3
[attr$=val] 属性以指定值结尾的 元素 3
[attr~=val] 属性蕴含指定值 (残缺单词) 的元素(不举荐应用) 2 ×
`[attr\ =val]` 属性以指定值 (残缺单词) 结尾的 元素(不举荐应用) 2 ×

伪元素

选择器 阐明 版本 罕用
::before 在元素前插入的 内容 2
::after 在元素后插入的 内容 2
::first-letter 元素的 首字母 1 ×
::first-line 元素的 首行 1 ×
::selection 鼠标选中的 元素 3 ×
::backdrop 全屏模式的 元素 4 ×
::placeholder 表单元素的 占位 4

选择器真正的用途不仅仅是 阐明选项 里的形容,更多是搭配起来能起到的最大作用。这些选择器组成的 选择器零碎 是整个 CSS 体系里的外围,应用选择器能带来以下益处。

  • 清晰易读 :对于那些构造与行为拆散的写法,应用sass/less 编写属性时构造会更清晰易读,缩小很多无用或少用的类,放弃 css 文件 的整洁性和观赏性
  • 确保统一:缩小批改类而有可能导致款式生效的问题,有时批改类但未确保 HTML 和 CSS 的统一而导致款式生效
  • 剔除累赘:缩小无实质性应用的类,例如很多层嵌套的标签,这些标签可能只应用到一个属性,就没必要新建类关联
  • 高效晦涩:应用选择器可实现一些看似只能由 JS 能力实现的成果,既缩小代码量也缩小 JS 对 DOM 的操作,使得交互成果更晦涩

浅谈布局那些事

把握一些罕用布局是一个前端必不可少的技能。养成看设计图就能大略布局出整体布局的前提是必须相熟这些罕用布局的特点与结构。已经需联合很多属性能力实现一个布局,现在在古代属性的加持下能更好地疾速实现各种布局,节约更多工夫去做更重要的事件。

全屏布局

经典的 全屏布局 顶部 底部 主体 三局部组成,其特点为 三局部左右满屏拉伸 顶部底部高度固定 主体高度自适应 。该布局很常见,也是大部分 Web 利用主体的支流布局。通常应用<header><footer><main>三个标签语义化排版,<main>内还可插入 <aside> 侧栏或其余语义化标签。

<div class="fullscreen-layout">
    <header></header>
    <main></main>
    <footer></footer>
</div>

position + left/right/top/bottom

三局部对立申明 left:0right:0将其左右满屏拉伸;顶部和底部别离申明 top:0bottom:0将其吸顶和吸底,并申明俩高度为固定值;将主体的 topbottom别离申明为顶部高度和底部高度。通过相对定位的形式将三局部固定在特定地位,使其互不影响。

.fullscreen-layout {
    position: relative;
    width: 400px;
    height: 400px;
    header,
    footer,
    main {
        position: absolute;
        left: 0;
        right: 0;
    }
    header {
        top: 0;
        height: 50px;
        background-color: #f66;
    }
    footer {
        bottom: 0;
        height: 50px;
        background-color: #66f;
    }
    main {
        top: 50px;
        bottom: 50px;
        background-color: #3c9;
    }
}

flex

应用 flex 实现会更简洁。display:flex默认会令子节点横向排列,需申明 flex-direction:column 扭转子节点排列方向为纵向排列;顶部和底部高度固定,所以主体需申明 flex:1 让高度自适应。

.fullscreen-layout {
    display: flex;
    flex-direction: column;
    width: 400px;
    height: 400px;
    header {
        height: 50px;
        background-color: #f66;
    }
    footer {
        height: 50px;
        background-color: #66f;
    }
    main {
        flex: 1;
        background-color: #3c9;
    }
}

<main> 需体现成可滚动状态,千万不要申明 overflow:auto 让容器自适应滚动,这样做有可能因为其余格式化上下文的影响而导致自适应滚动生效或产生其余未知成果。需在 <main> 内插入一个 <div> 并申明如下。

div {
    overflow: hidden;
    height: 100%;
}
多列布局
两列布局

经典的 两列布局 左右两列 组成,其特点为 一列宽度固定 另一列宽度自适应 两列高度固定且相等。以下以左列宽度固定和右列宽度自适应为例,反之同理。

<div class="two-column-layout">
    <div class="left"></div>
    <div class="right"></div>
</div>

float + margin-left/right

左列申明 float:left 和固定宽度,因为 float 使节点脱流,右列需申明 margin-left 为左列宽度,以保障两列不会重叠。

.two-column-layout {
    width: 400px;
    height: 400px;
    .left {
        float: left;
        width: 100px;
        height: 100%;
        background-color: #f66;
    }
    .right {
        margin-left: 100px;
        height: 100%;
        background-color: #66f;
    }
}

overflow + float

左列申明同上,右列申明 overflow:hidden 使其造成 BFC 区域 与外界隔离。BFC相干详情请查看小册第 4 章盒模型。

.two-column-layout {
    width: 400px;
    height: 400px;
    .left {
        float: left;
        width: 100px;
        height: 100%;
        background-color: #f66;
    }
    .right {
        overflow: hidden;
        height: 100%;
        background-color: #66f;
    }
}

flex

应用 flex 实现会更简洁。左列申明固定宽度,右列申明 flex:1 自适应宽度。

.two-column-layout {
    display: flex;
    width: 400px;
    height: 400px;
    .left {
        width: 100px;
        background-color: #f66;
    }
    .right {
        flex: 1;
        background-color: #66f;
    }
}
三列布局

经典的 三列布局 左中右三列 组成,其特点为 间断两列宽度固定 残余一列宽度自适应 三列高度固定且相等。以下以左中列宽度固定和右列宽度自适应为例,反之同理。整体的实现原理与上述两列布局统一。

<div class="three-column-layout">
    <div class="left"></div>
    <div class="center"></div>
    <div class="right"></div>
</div>

为了让右列宽度自适应计算,就不应用 float + margin-left 的形式了,若应用 margin-left 还得联合左中列宽度计算。

overflow + float

.three-column-layout {
    width: 400px;
    height: 400px;
    .left {
        float: left;
        width: 50px;
        height: 100%;
        background-color: #f66;
    }
    .center {
        float: left;
        width: 100px;
        height: 100%;
        background-color: #66f;
    }
    .right {
        overflow: hidden;
        height: 100%;
        background-color: #3c9;
    }
}

flex

.three-column-layout {
    display: flex;
    width: 400px;
    height: 400px;
    .left {
        width: 50px;
        background-color: #f66;
    }
    .center {
        width: 100px;
        background-color: #66f;
    }
    .right {
        flex: 1;
        background-color: #3c9;
    }
}
圣杯布局与双飞翼布局

经典的 圣杯布局 双飞翼布局 都是由 左中右三列 组成,其特点为 左右两列宽度固定 两头一列宽度自适应 三列高度固定且相等。其实也是上述两列布局和三列布局的变体,整体的实现原理与上述 N 列布局统一,可能就是一些细节需注意。

圣杯布局 双飞翼布局 在大体雷同下也存在一点不同,区别在于 双飞翼布局 两头列需插入一个子节点。在惯例的实现形式中也是在这个两头列里做文章,如何使两头列内容不被左右列遮挡

  • 雷同

    • 两头列放首位且申明其宽高占满父节点
    • 被挤出的左右列应用 floatmargin 负值 将其拉回与两头列处在同一水平线上
  • 不同

    • 圣杯布局:父节点申明 padding 为左右列留出空位,将左右列固定在空位上
    • 双飞翼布局:两头列插入子节点并申明 margin 为左右列让出空位,将左右列固定在空位上

圣杯布局 float + margin-left/right + padding-left/right

因为浮动节点在地位上不能高于后面或平级的非浮动节点,否则会导致浮动节点下沉。因而在编写 HTML 构造时,将两头列节点挪到右列节点前面。

<div class="grail-layout-x">
    <div class="left"></div>
    <div class="right"></div>
    <div class="center"></div>
</div>
.grail-layout-x {
    padding: 0 100px;
    width: 400px;
    height: 400px;
    .left {
        float: left;
        margin-left: -100px;
        width: 100px;
        height: 100%;
        background-color: #f66;
    }
    .right {
        float: right;
        margin-right: -100px;
        width: 100px;
        height: 100%;
        background-color: #66f;
    }
    .center {
        height: 100%;
        background-color: #3c9;
    }
}

双飞翼布局 float + margin-left/right

HTML 构造大体同上,只是在两头列里插入一个子节点<div>。依据两者区别,CSS 申明会与上述圣杯布局有一点点出入,可察看比照找出不同中央。

<div class="grail-layout-y">
    <div class="left"></div>
    <div class="right"></div>
    <div class="center">
        <div></div>
    </div>
</div>
.grail-layout-y {
    width: 400px;
    height: 400px;
    .left {
        float: left;
        width: 100px;
        height: 100%;
        background-color: #f66;
    }
    .right {
        float: right;
        width: 100px;
        height: 100%;
        background-color: #66f;
    }
    .center {
        margin: 0 100px;
        height: 100%;
        background-color: #3c9;
    }
}

圣杯布局 / 双飞翼布局 flex

应用 flex 实现 圣杯布局 / 双飞翼布局 可疏忽上述剖析,左右两列宽度固定,两头列宽度自适应。

<div class="grail-layout">
    <div class="left"></div>
    <div class="center"></div>
    <div class="right"></div>
</div>
.grail-layout {
    display: flex;
    width: 400px;
    height: 400px;
    .left {
        width: 100px;
        background-color: #f66;
    }
    .center {
        flex: 1;
        background-color: #3c9;
    }
    .right {
        width: 100px;
        background-color: #66f;
    }
}
均分布局

经典的 均分布局 多列 组成,其特点为 每列宽度相等 每列高度固定且相等。总体来说也是最简略的经典布局,因为每列宽度相等,所以很易找到适合的形式解决。

<div class="average-layout">
    <div class="one"></div>
    <div class="two"></div>
    <div class="three"></div>
    <div class="four"></div>
</div>
.one {background-color: #f66;}
.two {background-color: #66f;}
.three {background-color: #f90;}
.four {background-color: #09f;}

float + width

每列宽度申明为相等的百分比,若有 4 列则声明 width:25%。N 列就用公式100 / n 求出最终百分比宽度,记得保留 2 位小数,懒人还可用 width:calc(100% / n) 主动计算呢。

.average-layout {
    width: 400px;
    height: 400px;
    div {
        float: left;
        width: 25%;
        height: 100%;
    }
}

flex

应用 flex 实现会更简洁。节点申明 display:flex 后,生成的 FFC 容器 里所有子节点的高度都相等,因为容器的 align-items 默认为 stretch,所有子节点将占满整个容器的高度。每列申明flex:1 自适应宽度。

.average-layout {
    display: flex;
    width: 400px;
    height: 400px;
    div {flex: 1;}
}
居中布局

居中布局 父容器 若干个子容器 组成,子容器在父容器中横向排列或竖向排列且呈程度居中或垂直居中。居中布局是一个很经典的问题,所以笔者在小册中列举了所有居中布局形式,详情请查看小册第 6 章布局形式。

在此间接上一个目前最简略最高效的居中形式。display:flexmargin:auto 的强行组合,同学们自行领会。

<div class="center-layout">
    <div></div>
</div>
.center-layout {
    display: flex;
    width: 400px;
    height: 400px;
    background-color: #f66;
    div {
        margin: auto;
        width: 100px;
        height: 100px;
        background-color: #66f;
    }
}

绘制三角的原理

盒模型从实践上来说是一个规范的矩形,很难将其联想到基于盒模型绘制一个三角形。当然存在一个叫 clip-path 的属性,可绘制三角形,鉴于其兼容性较差通常不会大范畴应用它绘制三角形。

很多同学都会基于盒模型编写三角形,但大部分都是复制粘贴的操作。从原理上正确理解其成因,能力无需复制粘贴就能得心应手地绘制各种三角形。以下从零到一相熟一次绘制三角形的原理。

绘制一个边框别离为四种色彩的正方形。

<div class="triangle"></div>
.triangle {
    border: 50px solid;
    border-left-color: #f66;
    border-right-color: #66f;
    border-top-color: #f90;
    border-bottom-color: #09f;
    width: 200px;
    height: 200px;
}

别离将 widthheight累减到0,发现正方形由四个不同色彩的等腰三角形组成。

.triangle {
    border: 50px solid;
    border-left-color: #f66;
    border-right-color: #66f;
    border-top-color: #f90;
    border-bottom-color: #09f;
    width: 0;
    height: 0;
}

尝试将左边框色彩申明为通明,会发现左边框暗藏起来。

.triangle {
    border: 50px solid;
    border-left-color: #f66;
    border-right-color: transparent;
    border-top-color: #f90;
    border-bottom-color: #09f;
    width: 0;
    height: 0;
}

同样原理,将上边框色彩和下边框色彩同时申明为通明,就会失去一个指向左边的三角形。

.triangle {
    border: 50px solid;
    border-left-color: #f66;
    border-right-color: transparent;
    border-top-color: transparent;
    border-bottom-color: transparent;
    width: 0;
    height: 0;
}

可简写成以下代码。仔细的同学可能还会发现三角形的宽是高的 2 倍,而高正好是边框宽度 border-width。从中可得出一个技巧: 若绘制三角形的方向为左右高低,则将四条边框色彩申明为通明且将指定方向的反方向的边框着色,即可失去所需方向的三角形

.triangle {
    border: 50px solid transparent;
    border-left-color: #f66;
    width: 0;
    height: 0;
}

若绘制左上角、左下角、右上角或右下角的三角形,应用上述技巧就无奈实现了。可略微变通思维,其实指向左上角的三角形是由左边框和上边框组成,其余三角形也是如此。

.triangle {
    border: 50px solid transparent;
    border-left-color: #f66;
    border-top-color: #f66;
    width: 0;
    height: 0;
}

基于上述原理,可得心应手绘制出左右高低、左上角、左下角、右上角和右下角的三角形了,再联合相对定位 (position/left/right/top/bottom)、边距(margin/margin-*) 或变换 (transform) 调整地位即可。

.triangle {
    border: 50px solid transparent;
    width: 0;
    height: 0;
    &.left {border-right-color: #f66;}
    &.right {border-left-color: #f66;}
    &.top {border-bottom-color: #f66;}
    &.bottom {border-top-color: #f66;}
    &.left-top {
        border-left-color: #f66;
        border-top-color: #f66;
    }
    &.left-bottom {
        border-left-color: #f66;
        border-bottom-color: #f66;
    }
    &.right-top {
        border-right-color: #f66;
        border-top-color: #f66;
    }
    &.right-bottom {
        border-right-color: #f66;
        border-bottom-color: #f66;
    }
}

完满极致的变量

变量 又名 自定义属性 ,指可在整个文档中重复使用的值。它由自定义属性--var 和函数 var() 组成,var()用于援用自定义属性。应用变量能带来以下益处。

  • 缩小款式代码的重复性
  • 减少款式代码的扩展性
  • 进步款式代码的灵活性
  • 增多一种 CSS 与 JS 的通信形式
  • 不必深层遍历 DOM 扭转某个款式

同时变量也是 浏览器原生个性 ,无需通过任何转译可间接运行,也是 DOM 对象,极大便当了 CSS 与 JS 间的分割。变量除了具备简洁性和复用性,在重构组件款式时能让代码更易管制,同时还暗藏了一个弱小的技巧,那就是与calc() 联合应用。

看看一个简略的例子。一个条形加载条通常由几条线条组成,每条线条对应一个存在不同时延的雷同动画,通过时间差运行雷同动画,从而产生加载成果。预计大部分同学可能会把代码编写成以下模式。

<ul class="strip-loading">
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
</ul>
.strip-loading {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 200px;
    height: 200px;
    li {
        border-radius: 3px;
        width: 6px;
        height: 30px;
        background-color: #f66;
        animation: beat 1s ease-in-out infinite;
        & + li {margin-left: 5px;}
        &:nth-child(2) {animation-delay: 200ms;}
        &:nth-child(3) {animation-delay: 400ms;}
        &:nth-child(4) {animation-delay: 600ms;}
        &:nth-child(5) {animation-delay: 800ms;}
        &:nth-child(6) {animation-delay: 1s;}
    }
}
@keyframes beat {
    0%,
    100% {transform: scaleY(1);
    }
    50% {transform: scaleY(.5);
    }
}

剖析代码发现,每个 <li> 只是 animation-delay 不同,其余代码则完全相同,换成其余相似的 List 汇合,那岂不是有 10 个<li> 就写 10 个:nth-child(n)。显然这种形式不灵便也不易封装成组件,若能像 JS 那样封装成一个函数,并依据参数输入不同款式成果,那就更棒了。

对于 HTML 局部的批改,让每个 <li> 领有一个本人作用域下的变量。对于 CSS 局部的批改,就需剖析哪些属性是随着 index 递增而产生法则变动的,对法则变动的局部应用变量表达式代替即可。当然以下 <li style="--line-index: n;"></li> 可用 React JSXVue Template的遍历语法编写。

<ul class="strip-loading">
    <li style="--line-index: 1;"></li>
    <li style="--line-index: 2;"></li>
    <li style="--line-index: 3;"></li>
    <li style="--line-index: 4;"></li>
    <li style="--line-index: 5;"></li>
    <li style="--line-index: 6;"></li>
</ul>
.strip-loading {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 200px;
    height: 200px;
    li {--time: calc((var(--line-index) - 1) * 200ms);
        border-radius: 3px;
        width: 6px;
        height: 30px;
        background-color: #f66;
        animation: beat 1.5s ease-in-out var(--time) infinite;
        & + li {margin-left: 5px;}
    }
}
@keyframes beat {
    0%,
    100% {transform: scaleY(1);
    }
    50% {transform: scaleY(.5);
    }
}

代码中的变量 --line-index--time使每个 <li> 领有一个属于本人的作用域。例如第二个 <li>--line-index 的值为 2,--time的计算值为 200ms,换成第三个<li> 后这两个值又会不同了。这就是变量的作用范畴所致 ( 在以后节点块作用域及其子节点块作用域下无效)。

calc(var(--line-index) * 200ms)就像一个 JS 函数,在以后节点的作用域上读取相应的变量,从而计算出具体数值并交由浏览器初始化。从中可得出一个技巧:List 汇合里具备与索引递增相干的属性值都可用变量与 calc() 联合应用生成进去

还记得小学时代学习圆周率的场景吗,已经有学者将一个圆形划分为很多很小的矩形,若这些矩形划分得足够细,那么也可拼在一起变成一个圆形。

将圆形划分为 360 个小矩形且每个矩形绝对于父容器相对定位,申明 transform-origincenter bottom将小矩形的变换基准变更为 最底部最两头,每个小矩形依照递增角度顺时针旋转 N 度,就会造成一个圆形。此时依照递增角度调整小矩形的背景色相,就会看到意想不到的突变成果了。

  • 每个小矩形的递增角度:--Θ:calc(var(--line-index) / var(--line-count) * 1turn)
  • 每个小矩形的背景色相:filter:hue-rotate(var(--Θ))
  • 每个小矩形的旋转角度:transform:rotate(var(--Θ))

若将小矩形的尺寸和数量设置更细更多,整体的突变成果就会更平均。

<ul class="gradient-circular" style="--line-count: 360;">
    <li style="--line-index: 1;"></li>
    ...
    <li style="--line-index: 360;"></li>
    <!-- 360 个 <li>,可用模板语法生成  -->
</ul>
.gradient-circular {
    position: relative;
    width: 4px;
    height: 200px;
    li {--Θ: calc(var(--line-index) / var(--line-count) * 1turn);
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        background-color: #3c9;
        filter: hue-rotate(var(--Θ));
        transform-origin: center bottom;
        transform: rotate(var(--Θ));
    }
}

添枝加叶的伪元素

有时为了实现某个成果而往页面里重复增加标签变得很繁琐,增加太多标签反而不好解决而变得难以保护。此时会引入 伪元素 这个概念解决上述问题。正是 伪元素 能解决一些可不增加其余标签而起到占位作用,笔者才称 伪元素 “添枝加叶”

上述选择器分类有提及 伪元素 ,广义上来说选择器除了 伪元素 ,其余都是 伪类 伪元素 伪类 尽管都是选择器,但它们还是存在一丝丝的差异。

  • 伪元素 通常是一些实体选择器,抉择满足指定条件的 DOM,例如 ::selection::first-letter::first-line
  • 伪类 通常是一些状态选择器,抉择处于特定状态的 DOM,例如 :hover:focus:checked

伪元素 指页面里不存在的元素。伪元素 在 HTML 代码里未声明却能失常显示,在页面渲染时看到这些原本不存在的元素施展着重要作用。:before:after 是两个很重要的 伪元素,早在 CSS2 就呈现了。

起初 伪元素 的前缀应用 单冒号语法 。随着 CSS 改革, 伪元素 的前缀被批改成 双冒号语法 :before/:after 从此变成 ::before/::after,用来辨别 伪类 ,未提及的 伪元素 同理。若兼容低版本浏览器,还需应用 :before:after

两者最次要的区别就是 伪类 应用 单冒号语法 伪元素 应用 双冒号语法 。当然笔者还是提倡同学们应用 单冒号语法 标记 伪类 ,应用 双冒号语法 标记 伪元素,这样在代码模式上就能一眼辨别进去。

::before::after 的应用场景很多,也是笔者着重钻研的技巧之一。::before/::after必须联合 content 应用,通常用作润饰节点,为节点插入一些多余的货色,但又不想内嵌一些其余标签。若插入 2 个以下 (蕴含 2 个) 的润饰,倡议应用::before/::after

说时迟那时快,立马联合上述绘制三角形的原理绘制一个罕用的气泡对话框,圆滚滚的身子带上一个三角形的尾巴。气泡对话框的身板就是一个圆角矩形,可用 <div> 间接绘制,小尾巴是一个三角形,可用 ::after 占位并绘制。这样就无需在 <div> 里增加一个 <i> 绘制小尾巴了。

<div class="bubble-box">iCSS</div>
.bubble-box {
    position: relative;
    border-radius: 5px;
    width: 200px;
    height: 50px;
    background-color: #f90;
    line-height: 50px;
    text-align: center;
    font-size: 20px;
    color: #fff;
    &::after {
        position: absolute;
        left: 100%;
        top: 50%;
        margin-top: -5px;
        border: 5px solid transparent;
        border-left-color: #f90;
        content: "";
    }
}

从中可得出一个技巧:若为节点做一些润饰却不想插入其余标签,可用::before 和::after 代替,但实用于 2 个占位以下 。其实这个也不算什么特地技巧,只是很多同学都不会去留神这种用法,有需要都是间接增加标签。兴许以下提及的 障眼法 内容插入 会让同学们对 伪元素 另眼相看。

灵便多变的障眼法

上述应用 ::after 简略地绘制气泡对话框的尾巴,然而简单一点的带边框气泡对话框是否也应用 伪元素 绘制呢。看到这里先不要往下看代码,自行思考 1 分钟想想实现办法。

答案当然是可行的。以下是整个带边框气泡对话框的拆解,整体由三局部组成:带边框圆角矩形、彩色三角形、橙色三角形。先将两个三角形错位叠加生成一个箭头状的图形,再将该图形叠加到带边框圆角矩形的左边,最初将彩色三角形着色成红色,就能失去上图的带边框气泡对话框了。

<div class="bubble-empty-box">iCSS</div>
.bubble-empty-box {
    position: relative;
    border: 2px solid #f90;
    border-radius: 5px;
    width: 200px;
    height: 50px;
    line-height: 46px;
    text-align: center;
    font-size: 20px;
    color: #f90;
    &::before {
        position: absolute;
        left: 100%;
        top: 50%;
        margin: -5px 0 0 2px;
        border: 5px solid transparent;
        border-left-color: #f90;
        content: "";
    }
    &::after {
        position: absolute;
        left: 100%;
        top: 50%;
        margin-top: -4px;
        border: 4px solid transparent;
        border-left-color: #fff;
        content: "";
    }
}

整体实现思路就是一种障眼法,正确来说就是将图形错位叠加产生另一种成果,在平面设计中叫做 占位叠加。有了这种设计思维,其实能应用 CSS 发明出很多动向不到的障眼法成果。

当你遇见心仪妹纸时,心里噗通噗通地跳动,此时此刻可用纯 CSS 描述你的情绪。应用单个 <div> 联合 ::before::after,通过错位叠加的形式生成一个心形。在叠加前看看以下图形,是不是发现很像米老鼠呢。

  • 申明 <div> 形态为 正方形 并以核心顺时针旋转45deg
  • 申明 ::before::after继承 <div> 尺寸并别离相对定位到左上角和右上角
  • 申明 ::before::after的圆角率为100%

<div class="heart-shape"></div>
.heart-shape {
    position: relative;
    width: 200px;
    height: 200px;
    background-color: #f66;
    transform: rotate(45deg);
    &::before,
    &::after {
        position: absolute;
        left: 0;
        top: 0;
        border-radius: 100%;
        width: 100%;
        height: 100%;
        background-color: #f66;
        content: "";
    }
    &::before {transform: translateX(-50%);
    }
    &::after {transform: translateY(-50%);
    }
}

最初奇妙利用 transform::before::after 平移到相应地位产生叠加错觉。这时别离对 ::before::after着色,看看其中的神秘。

在这个根底上来一个更高级的玩法,增加突变成果让心形变得更么么哒。

  • 申明 <div> 从上到下 (实际效果是从右上角到左下角) 突变着色
  • 因为 ::before 从旋转后的 <div> X 轴往左平移过来,所以其着色成果与<div> 统一
  • 因为 ::after 从旋转后的 <div> Y 轴往上平移过来,所以其中线地位突变着色必须与<div> 顶部突变着色的色彩统一(具体往下剖析)

整体突变成果的重点在 ::after 上,因为 ::after 下半部叠加在 <div> 上,所以下半部色彩必须通明,上半部底部 (中线地位) 突变着色必须与 <div> 顶部突变着色的色彩统一,这样能力做到无缝连接。通过 Windows 零碎MacOS 零碎 的测试,在 Windows 零碎 下的通明突变地位需在 51% 的中央开始,这与屏幕设施的分辨率和广色域无关。

最初为了让突变心形看起来更具立体感,给它绘制个暗影吧。若感觉这个突变动感心形很美,可顺手转发给女友哇!

<div class="gradient-heart-shape"></div>
.gradient-heart-shape {
    position: relative;
    width: 200px;
    height: 200px;
    background-image: linear-gradient(to bottom, #09f, #f66);
    box-shadow: 0 0 20px rgba(#000, .8);
    transform: rotate(45deg);
    &::before,
    &::after {
        position: absolute;
        left: 0;
        top: 0;
        border-radius: 100%;
        width: 100%;
        height: 100%;
        content: "";
    }
    &::before {background-image: linear-gradient(to bottom, #09f, #f66);
        transform: translateX(-50%);
    }
    &::after {background-image: linear-gradient(to bottom, #3c9, #09f 50%, transparent 50%, transparent);
        transform: translateY(-50%);
    }
}

动向不到的内容插入

上述提到 ::before/::after 必须联合 content 应用,那么 content 就真的只能插入一般字符串吗?content何止这么简略,以下推广几种少见但弱小的内容插入技巧。通过这几种技巧,就能很不便地将读取到的数据动静插入到 ::before::after中。

  • 内容拼接
  • 联合 attr() 应用
  • 联合 变量 计数器 应用

内容拼接

惯例操作是 content:"CSS",也可拼接多个字符串,有些同学可能第一工夫想起content:"Hello"+"CSS"。托付,这不是 JS 而是 CSS,CSS 字符串拼接当然有本人的规定。CSS 字符串拼接既不能应用+ 相连也可不必 空格 距离。

.elem {
    content: "Hello""CSS"; // 等价于"Hello ""CSS"
    content: "Hello" attr(data-name); // 与 attr()拼接
    content: counter(progress) "%"; // 与 counter()拼接}

联合 attr() 应用

attr()是一个被疏忽的选择器,它有着弱小的属性捕捉性能。有这么一个场景,一个数据汇合需遍历到每个 DOM 上并把某个字段插入到其 ::after 上。这该怎么办,如同 95% 的同学都不会应用 JS 获取节点的 ::before::after。这时 attr() 就派上用场了。

<li v-for="v in list" :key="v.id" :data-name="v.name">
li::after {content: attr(data-name);
}

一行 CSS 代码搞掂,还用什么 JS 去获取节点的 ::after 呢。当然 contentattr()的应用场景不止那一点。

:hover作用于鼠标悬浮的节点,是一个很好用的选择器。在特定场景可代替 mouseentermouseleave两个鼠标事件,加上 transtion 让节点的动画更丝滑。联合 attr() 有一个很好用的场景,就是鼠标悬浮在某个节点上显示提醒浮层,提醒浮层里蕴含着该动作的文本。

  • 给节点标记一个用户属性data-*
  • 当鼠标悬浮在该节点上触发:hover
  • 通过 attr() 获取 data-* 的内容
  • data-* 的内容赋值到 伪元素 content

<ul class="hover-tips">
    <li data-name="姨妈红"></li>
    <li data-name="基佬紫"></li>
    <li data-name="箩底橙"></li>
    <li data-name="姣婆蓝"></li>
    <li data-name="大粪青"></li>
    <li data-name="原谅绿"></li>
</ul>
$color-list: #f66 #66f #f90 #09f #9c3 #3c9;
.hover-tips {
    display: flex;
    justify-content: space-between;
    width: 200px;
    li {
        position: relative;
        padding: 2px;
        border: 2px solid transparent;
        border-radius: 100%;
        width: 24px;
        height: 24px;
        background-clip: content-box;
        cursor: pointer;
        transition: all 300ms;
        &::before,
        &::after {
            position: absolute;
            left: 50%;
            bottom: 100%;
            opacity: 0;
            transform: translate3d(0, -30px, 0);
            transition: all 300ms;
        }
        &::before {
            margin: 0 0 12px -35px;
            border-radius: 5px;
            width: 70px;
            height: 30px;
            background-color: rgba(#000, .5);
            line-height: 30px;
            text-align: center;
            color: #fff;
            content: attr(data-name);
        }
        &::after {
            margin-left: -6px;
            border: 6px solid transparent;
            border-top-color: rgba(#000, .5);
            width: 0;
            height: 0;
            content: "";
        }
        @each $color in $color-list {$index: index($color-list, $color);
            &:nth-child(#{$index}) {
                background-color: $color;
                &:hover {border-color: $color;}
            }
        }
        &:hover {
            &::before,
            &::after {
                opacity: 1;
                transform: translate3d(0, 0, 0);
            }
        }
    }
}

联合 变量 计数器 应用

当初来玩高级一点的货色,先不做任何铺垫,接着往下看即可,反正就是 content 联合 变量 计数器 的应用场景。

笔者想做一个实时显示进度的悬浮球,跟着笔者一起敲代码吧。先画一个绿油油的波波。

<div class="state-ball">
    <div class="wave"></div>
</div>
.state-ball {
    overflow: hidden;
    position: relative;
    padding: 5px;
    border: 3px solid #3c9;
    border-radius: 100%;
    width: 150px;
    height: 150px;
    background-color: #fff;
    .wave {
        position: relative;
        border-radius: 100%;
        width: 100%;
        height: 100%;
        background-image: linear-gradient(to bottom, #af8 13%, #3c9 91%);
    }
}

进度通常都是从底部往顶部逐步晋升,可用 ::before 绘制一个圆形遮罩层,进度变动时将遮罩层始终往上晋升产生障眼成果。晋升过程可用相对定位将遮罩层固定在底部,通过调整 margin-bottom 平移遮罩层。

为了不便演示,正文父容器的 overflow:hidden,通过Chrome Devtools 微调 margin-bottom 看看整体成果。后续记得将 overflow:hidden 申明回来。

.state-ball {
    // overflow: hidden;
    // ...
    &::before {
        position: absolute;
        left: 50%;
        bottom: 5px;
        z-index: 9;
        margin-left: -100px;
        margin-bottom: 0;
        border-radius: 100%;
        width: 200px;
        height: 200px;
        background-color: #09f;
        content: "";
    }
    // ...
}

为了让晋升过程出现动态效果,调整 ::before 的背景色彩和圆角率并追加一个旋转动画。

.state-ball {
    // ...
    &::before {
        position: absolute;
        left: 50%;
        bottom: 5px;
        z-index: 9;
        margin-left: -100px;
        margin-bottom: 0;
        border-radius: 45%;
        width: 200px;
        height: 200px;
        background-color: rgba(#fff, .5);
        content: "";
        animation: rotate 10s linear -5s infinite;
    }
    // ...
}
@keyframes rotate {
    to {transform: rotate(1turn);
    }
}

为了让波浪出现平面成果,追加 ::after 占位并申明整体款式与 ::before 统一,在背景色彩、圆角率和动画时延上略有差别即可。另外申明 ::aftermargin-bottom略微比 ::before 高一点,这样在旋转过程中能让波浪产生动静的平面成果。

在晋升过程中,两个遮罩层位移间隔应该是统一的,所以可用变量计算公式示意且 ::after::before10px。在这里有个值得注意的中央,若变量联合calc() 应用,其后果必须带上单位,以这两条公式为例,其变量初始值必须为--offset:0px,不能为--offset:0

  • ::beforemargin-bottom:var(--offset)
  • ::aftermargin-bottom:calc(var(--offset) + 10px)

<div class="state-ball" style="--offset: 0px;">
    <div class="wave"></div>
</div>
.state-ball {
    // ...
    &::before,
    &::after {
        position: absolute;
        left: 50%;
        bottom: 5px;
        z-index: 9;
        margin-left: -100px;
        width: 200px;
        height: 200px;
        content: "";
    }
    &::before {margin-bottom: var(--offset);
        border-radius: 45%;
        background-color: rgba(#fff, .5);
        animation: rotate 10s linear -5s infinite;
    }
    &::after {margin-bottom: calc(var(--offset) + 10px);
        border-radius: 40%;
        background-color: rgba(#fff, .8);
        animation: rotate 15s infinite;
    }
    // ...
}
// ...

到此再优化一些细节,通过 Chrome Devtools 查看 .wave 得悉其尺寸为 134x134,每次往上平移两个伪元素只能1px 那样递增。当初想将其平移 100 次就能填充整个球体,那么就需依照 134/100 这个比例革新变量计算公式。

--offset 申明为 --offset:0,取值区间在0~100 而不是0px~100px

  • ::beforemargin-bottom:calc(var(--offset) * 1.34px)
  • ::aftermargin-bottom:calc(var(--offset) * 1.34px + 10px)

<div class="state-ball" style="--offset: 0;">
    <div class="wave"></div>
</div>
.state-ball {
    // ...
    &::before {margin-bottom: calc(var(--offset) * 1.34px)
        // ...
    }
    &::after {margin-bottom: calc(var(--offset) * 1.34px + 10px);
        // ...
    }
    // ...
}
// ...

当初已把位移间隔管制在 0~100 的比例了,那么剩下步骤就是追加一个 <div>,应用其content 寄存在 offset 实时显示进度了。

<div class="state-ball" style="--offset: 0;">
    <div class="wave"></div>
    <div class="progress"></div>
</div>
.state-ball {
    // ...
    .progress::after {
        display: flex;
        position: absolute;
        left: 0;
        top: 0;
        z-index: 99;
        justify-content: center;
        align-items: center;
        width: 100%;
        height: 100%;
        font-weight: bold;
        font-size: 16px;
        color: #09f;
        content: var(--offset) "%";
    }
}
// ...

可是发现无任何文本成果。状况是这样的,若变量是字符串类型可间接显示,若变量是数值类型则需借助 counter() 显示。而 counter() 还需应用 counter-reset 初始默认值,CSS 计数器怎么用在这里就不解说了,感兴趣的同学可自行百度。

整体革新工程就这样实现了,残缺代码如下。最初通过 JS 操作变量 --offset 就能实时扭转进度了。

<div class="state-ball" style="--offset: 0;">
    <div class="wave"></div>
    <div class="progress"></div>
</div>
.state-ball {
    overflow: hidden;
    position: relative;
    padding: 5px;
    border: 3px solid #3c9;
    border-radius: 100%;
    width: 150px;
    height: 150px;
    background-color: #fff;
    &::before,
    &::after {
        position: absolute;
        left: 50%;
        bottom: 5px;
        z-index: 9;
        margin-left: -100px;
        width: 200px;
        height: 200px;
        content: "";
    }
    &::before {margin-bottom: calc(var(--offset) * 1.34px);
        border-radius: 45%;
        background-color: rgba(#fff, .5);
        animation: rotate 10s linear -5s infinite;
    }
    &::after {margin-bottom: calc(var(--offset) * 1.34px + 10px);
        border-radius: 40%;
        background-color: rgba(#fff, .8);
        animation: rotate 15s infinite;
    }
    .wave {
        position: relative;
        border-radius: 100%;
        width: 100%;
        height: 100%;
        background-image: linear-gradient(to bottom, #af8 13%, #3c9 91%);
    }
    .progress::after {
        display: flex;
        position: absolute;
        left: 0;
        top: 0;
        z-index: 99;
        justify-content: center;
        align-items: center;
        width: 100%;
        height: 100%;
        font-weight: bold;
        font-size: 16px;
        color: #09f;
        content: counter(progress) "%";
        counter-reset: progress var(--offset);
    }
}
@keyframes rotate {
    to {transform: rotate(1turn);
    }
}

无所不能的模仿点击事件

:checked作用于选项选中的表单节点,当 <input>type设置成 radiocheckbox时可用。很多同学都会应用 input:checked + div {}input:checked ~ div {}的操作模仿鼠标点击事件。要让 input:checked + div {}input:checked ~ div {}起效,其 HTML 构造必须像以下那样。

<input type="radio">
<div></div>

这样就无奈拆散构造与行为了,导致 CSS 必须跟着 HTML 走,只能应用相对定位将 <input> 固定到指定地位。应用 <label> 绑定 <input> 可将 <input> 的鼠标抉择事件转移到 <label> 上,由 <label> 管制选中状态。那么 HTML 构造可改为以下那样,此时的 <input> 可设置 hidden 暗藏起来,不参加任何排版。

<input type="radio" id="btn" hidden>
<div>
    <label for="btn">
</div>

<input>应用 id<label>应用 for 关联起来,而 hidden 使<input>暗藏起来,不占用页面任何地位,此时 <label> 搁置在页面任何地位都行。

input:checked + div {}
input:checked ~ div {}

有了这样的思路,就很易实现一个纯 CSS 标签导航了。

<div class="tab-navbar">
    <input id="tab1" type="radio" name="tabs" hidden checked>
    <input id="tab2" type="radio" name="tabs" hidden>
    <input id="tab3" type="radio" name="tabs" hidden>
    <input id="tab4" type="radio" name="tabs" hidden>
    <nav>
        <label for="tab1"> 题目 1 </label>
        <label for="tab2"> 题目 2 </label>
        <label for="tab3"> 题目 3 </label>
        <label for="tab4"> 题目 4 </label>
    </nav>
    <main>
        <ul>
            <li> 内容 1 </li>
            <li> 内容 2 </li>
            <li> 内容 3 </li>
            <li> 内容 4 </li>
        </ul>
    </main>
</div>
.active {
    background-color: #3c9;
    color: #fff;
}
.tab-navbar {
    display: flex;
    overflow: hidden;
    flex-direction: column-reverse;
    border-radius: 10px;
    width: 300px;
    height: 400px;
    input {&:nth-child(1):checked {& ~ nav label:nth-child(1) {@extend .active;}
            & ~ main ul {
                background-color: #f66;
                transform: translate3d(0, 0, 0);
            }
        }
        &:nth-child(2):checked {& ~ nav label:nth-child(2) {@extend .active;}
            & ~ main ul {
                background-color: #66f;
                transform: translate3d(-25%, 0, 0);
            }
        }
        &:nth-child(3):checked {& ~ nav label:nth-child(3) {@extend .active;}
            & ~ main ul {
                background-color: #f90;
                transform: translate3d(-50%, 0, 0);
            }
        }
        &:nth-child(4):checked {& ~ nav label:nth-child(4) {@extend .active;}
            & ~ main ul {
                background-color: #09f;
                transform: translate3d(-75%, 0, 0);
            }
        }
    }
    nav {
        display: flex;
        height: 40px;
        background-color: #f0f0f0;
        line-height: 40px;
        text-align: center;
        label {
            flex: 1;
            cursor: pointer;
            transition: all 300ms;
        }
    }
    main {
        flex: 1;
        ul {
            display: flex;
            flex-wrap: nowrap;
            width: 400%;
            height: 100%;
            transition: all 300ms;
        }
        li {
            display: flex;
            justify-content: center;
            align-items: center;
            flex: 1;
            font-weight: bold;
            font-size: 20px;
            color: #fff;
        }
    }
}

笔者已经发表过一篇《纯 CSS 收费让掘金社区领有暗黑模式切换性能》,探讨了 :checked+/~filter的玩法,详情请查看原文,在此就不啰嗦了。

总结

来不及分享的内容,就用文章叙述完,那天看直播的掘友们,让你们久等了。这几年花了很多工夫钻研 CSS,兴许写完本文就要对 CSS 告一段落了。尽管花了很多工夫钻研 CSS,但也公布了几篇爆款 CSS 文章和一本 CSS 掘金小册,也算是留下了这几年的 CSS 学习成绩了。

喜爱做的事件总不想留什么遗憾 。接下来也要将钻研方向转移到 JS 上了,还是会像钻研 CSS 那样认真钻研 JS 的 性能优化 设计模式 数据算法 三大装逼套件。冀望在 2021 年能有新的冲破吧,也感激掘金社区让我学习到他人的常识和他人学习到我的常识。

分享源码寄存在笔者的 Github 上,有须要的同学可拷贝一份。还有就是笔者向 可恶丑陋的小册姐姐 要了 100 份玩转 CSS 的艺术之美六折优惠码WmOrR0hR,对该小册感兴趣的同学可理解一下哟!

CSS 是一门天马行空的语言,说它简略也行说它艰难也行。想理解更多纯 CSS 特效,可回看笔者往期文章,也可浏览笔者集体官网的纯 CSS 特效专辑,保障满足你的眼球。

  • 灵活运用 CSS 开发技巧:4200+点赞量,125k+浏览量
  • 妙用 CSS 变量,让你的 CSS 变得更心动:500+点赞量,15k+浏览量
  • 纯 CSS 收费让掘金社区领有暗黑模式切换性能:100+点赞量,5k+浏览量

结语

❤️关注 + 点赞 + 珍藏 + 评论 + 转发❤️,原创不易,激励笔者创作更多高质量文章

关注公众号IQ 前端,一个专一于 CSS/JS 开发技巧的前端公众号,更多前端小干货等着你喔

退出移动版