前言
记得 2016 年刚参加工作那会,进的第一家公司的前端分组是有细分切图和 JS 两个方向。前端界面一般是由 UI 设计师出图后,由切图组来还原页面,完成后再交给 JS 人员进行功能开发。
要想尽可能的还原设计图,中间的细节还是很多的。当时切图组有着自己的规定和技巧,能够快速产出高还原度的页面和高质量的 HTML、CSS 代码。
最初 CSS 写法野生的我代码会存在下面问题:
- Modal, Popup, Tip 的 z-index 混乱
- important 滥用
- 布局自适应差
- ...
经过切图组的历练,代码规范和质量都提升不少,同时也能粗略看出别人 CSS 编写的水平。
本文中 CSS 前预处理器使用的 Less。
代码规范
定义变量
定义若干 Less 变量,提高代码的维护性,规范和统一样式。
- 主题色调。主色调:@primary,辅助色:@sub,警告色:@warn
- 其他色调。如边框,背景,字体颜色
- 尺寸类型。圆角,字号,通用高度(按钮,输入框)
- 统一样式。阴影 box-shadow
- 规范性数值。z-index
一般情况下我们都是使用第三方组件库进行开发,组件库已经完成了这些变量的定义,所以我们只需定义一些自己项目的通用变量即可。
使用指令化繁为简
通过 Less 可以定义通用指令,解决一些重复性工作,化繁为简,规范写法,便于后期维护。
- 文本超出显示省略号
.tof() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.tof2(@line) {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: @line;
}
- 多倍图使用
.bg-image(@url) {
background-image: url("@{url}@2x.png");
background-size: 100%;
background-repeat: no-repeat;
@media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) {
background-image: url("@{url}@3x.png");
}
}
代码风格
代码规范各个团队会有所不同,定义出自己的风格就行。
- 为了保证一致性和可扩展性,每个声明应该用分号结束(包括最后一条声明),每个声明换行
- 声明块的右花括号应当单独成行
- 当一个规则包含多个选择器时,每个选择器声明必须独占一行
- 在文件结尾添加一个空白行
- 缩进:使用 2 个空格做为一个缩进层级
// bad
.exa2, .exa3:focus, .exa4 {
display: inline-block; width: 24px; color: gray;}
// good
.exa2,
.exa3:focus,
.exa4 {
display: inline-block;
width: 24px; color: gray;
}
虽然最后样式都被压缩成无空格无换行的紧凑型,但代码的规范性直接影响着美观、团队协作效率。可以将规则加入 stylelint
属性顺序
团队内有统一的属性顺序规范会让你在编写或调试 CSS 时感到愉悦。统一的属性顺序好处也很明显:提高代码的可读性,便于查找。
根据属性的重要性按顺序书写,先写定位布局类属性,后写盒模型等自身属性,最后是文本类及修饰类属性
| 定位布局 | 自身属性 | 文本类及修饰类 |
|---|---|---|
| display | width | font |
| position | height | text-align |
| float | margin | white-space |
| top | padding | background |
| ... | ... | ... |
// bad
.box {
font-family: 'Arial', sans-serif;
border: 3px solid #ddd;
left: 30%;
position: absolute;
background-color: #eee;
right: 30%;
display: block;
font-size: 24px;
overflow: hidden;
padding: 1rem;
}
// good
.box {
display: block; // 定位布局
position: absolute;
left: 30%;
right: 30%;
overflow: hidden;
padding: 1rem; // 自身属性
border: 3px solid #ddd;
background-color: #eee;
font-size: 24px; // 文本类及修饰类
font-family: 'Arial', sans-serif;
}
当然,实际编写中不可能完全按定义好的顺序来写,不过大概是按上面三类来书写,最后可以使用 stylelint、stylelint-order 来格式化一下
嵌套层级
选择器的嵌套层级应不大于 3 级,位置靠后的限定条件应尽可能精确。
除了模块名和作用对象之外,中间的用于控制作用范围的选择器可以尽可能的少,只要能与其它的定义区分开即可,并不一定非得把所有的层级都写进去。这样可以更好的减少权值的大小 ,有利于后继的重定义。
这个早期我在切图组开发的时候,使用的是 BEM 规范。
// 普通
<div class="news">
<div class="news_listwrap">
<ol class="news_list">
<li class="news_item">
<span class="title"></span>
<span class="time"></span>
<li>
<ol>
<div>
<div>
.news .news_list .time {}
// BEM
<div class="news">
<div class="news__listwrap">
<ol class="news__list">
<li class="news__item">
<span class="news__title"></span>
<span class="news__time"></span>
<li>
<ol>
<div>
<div>
.news__time {}
现在开发一般都使用 CSS Modules,CSS Modules 能有效的避免样式冲突,所以一般情况我们都不会嵌套超过三层。
正确使用标签
想起我早期的切图作品,里面的 html 标签就是 div 一把写。正确的使用 html 标签也是让你的代码阅读性提高很多,也有助于 SEO。
一些常见的注意点:
- div 无任何语义,最好只用于页面的布局和容器。
- 尽量不在行内元素里套块级元素,如:<span><p></p></span>
- 一个页面只能使用一个 <h1> 标签
- 尽量不要为了样式写空标签,可以用 :before 和 :after 来控制,图片按钮之类除外。
- ul 和 li 之间不能有其他标签,虽然不会报错,但他们是配对的呀,不能有第三者
减少使用标签选择器
减少使用标签选择器,使用具有语义的 class 类名。从分离的角度考虑,在表现层中不应该分配 html 标记/语义。
有时我们会把一个有序列表需要被改成一个无序列表,或者一个 div 将被转换成 article。使用标签选择器的话到时 html 和 css 都要修改,增加工作量。
// bad
div.exa1 header.exa2 > h2 {
font-size: 28px;
}
//good
.exa1 .exa2 > .exa3 {
font-size: 28px;
}
合理使用 z-index 值
将 z-index 进行分层,对文档流外绝对定位元素的视觉层级关系进行管理。z-index 值不易设置过高。 同层的多个元素,如多个由用户输入触发 的Dialog,在该层级内使用相同的 z-index 或递增 z-index。 建议每层包含100个 z-index 来容纳足够的元素,如果每层元素较多,可以调整这个数值。
@zindex-fix: 100; // fixed, topbar, backtotop
@zindex-dd: 500; // dropdown
@zindex-modal: 1000 // pop
使用第三方组件开发的话,组件库一般已经设置好合理的 z-index 数值,直接使用就行。
处理样式自适应能力
如果你直接复制设计稿的相关属性,那你的样式自适应能力有时就比较差。比如设计稿一个宽度为 120px 的按钮,你没有自适应相关的意识的话,直接写死了 120px, 要是按钮文字比设计稿多,这时样式就出问题了.
// bad
.btn {
width: 120px;
}
// good
.btn {
min-width: 120px;
}
媒体查询
Media Query 不要单独编排,需要与相关的规则一起定义
// bad
.header {}
.content {}
@media (...) {
.header {}
.content {}
}
// good
.header {
@media (...) {}
}
.content {
@media (...) {}
}
布局的正确姿势
页面级布局 —— 顶、底部固定
/** 常规布局 */
.ui-layout {
position: relative;
padding-top: 64px;
padding-bottom: 64px;
overflow: hidden;
box-sizing: border-box;
.ui-layout__header {
position: absolute;
left: 0;
top: 0;
right: 0;
height: 64px;
}
.ui-layout__body {
height: 100%;
overflow-y: auto;
overflow-x: hidden;
}
.ui-layout__footer {
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 64px;
}
}
/** flex 布局 */
.ui-layout {
display: flex;
flex-direction: column;
.ui-layout__header {
height: 64px;
}
.ui-layout__body {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
}
.ui-layout__footer {
height: 64px;
}
}
页面级布局 —— 底部固定
内容少时,底部固定在最下方;内容多时,底部跟随内容滚动
.ui-layout {
position: relative;
min-height: 100%;
padding-bottom: 64px;
box-sizing: border-box;
.ui-layout__footer {
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 64px;
}
}
常见效果的写法
水平列表出现滚动条
<div class="wrap">
<ul class="box">
<li>1</li>
<li>2</li>
</ul>
<div>
.wrap {
overflow-x: auto;
overflow-y: hidden;
}
.box {
white-space: nowrap;
li {
display: inline-block;
width: 50px;
height: 50px;
}
}
长宽等比
需求1:实现一个长宽必须要等比(如16:9)的容器
需求2:一个元素宽度为屏幕宽度的50%,高度等于宽度,宽高比为1:1(一个正方形)
实现方式:基于容器 width 给 padding 一个百分比。如实现正方形的容器,设置 padding-top: 100%
实现原理:元素的 padding-top 或 padding-bottom 的百分比值是根据父元素的 width 进行计算的。
条件:元素 height: 0,且 box-sizing: border-box;
举例:实现 16:9 的容器:9 / 16 * 100% = 56.25%
.wrap {
width: 200px;
}
.box {
position: relative;
width: 100%;
padding-top: 100%;
overflow: hidden;
.content {
position :absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
}
}
你也可以直接使用 aspect-ratio 来实现,不过需要处理兼容性问题,可以引入 postcss-aspect-ratio-polyfill 处理
.box {
width: 200px;
aspect-ratio: 1/1;
}
总结
早期的 css 很多布局和兼容性都要花很多时间和耐心去处理。不过随着现代浏览器快速迭代,css 新特性的直接使用让我们可以快速写出页面,比如 flex 布局。flex 布局出现后,大部分页面上的页面结构都可以使用 flex 布局来完成。不过旧的方式也需要去了解,在了解的过程中会对 css 各个属性有更深理解,也会学到像 BFC 这样的概念。
总之,CSS 编写规范还是很重要的,有利于团队协作和代码维护。
本文正在参加「金石计划」