CSS

前言

当前CSS开发的现状不容乐观,扫了一圈,发现各种问题。前端开发更多关注点还是在JavaScript上,技术性相对更强。
但从前端技术的根本价值出发,实现高可用性的产品用户界面,是用户体验的第一道关,这就跟CSS开发的专业性紧密相关了。轻易改变一下窗囗大小,放大一下字体,页面就被拉垮,这样的产品品质高吗?
本文将涉及一些响应式开发内容,TOB产品同样需要响应式开发。以云安全中心为例,用户屏幕分辩率占比中1920x1080和2560x1440加起来达到52%。平时开发的15寸的本物理分辩率是1440x900只占9%。说明企业用户大多用PC或用外接显示器。请注意:屏幕物理分辨率≠浏览器窗囗大小(如下图)。平时用外接显示器会有各种用法,横着用,坚着用,分屏显示窗囗等等(如封面)。因此,不能简单的依据屏幕分辨率进行设计和开发。体现CSS开发专业性看的就是防御式CSS开发。
20 个防御式 CSS 开发经验 - 图120 个防御式 CSS 开发经验 - 图2
(数据来自css-tricks)
“防御性编程(Defensive programming)是防御式设计的一种具体体现,它是为了保证,对程序的不可预见的使用,不会造成程序功能上的损坏。它可以被看作是为了减少或消除墨菲定律效力的想法。”(引自wiki)
现在前端开发还是契约式的,也就是还原设计稿,这是远远不够的。设计稿往往只体现出UI的理想态。防御式CSS开发一部分是为了实现响应式设计,同时还包括适配动态内容,在各种情景下保持UI的完美性和健壮性。

1、采用扁平化的HTML结构,用CSS控制布局

1-1. 避免用“布局组件”

这样会把模块限死在HTML结构中,不利于灵活的适配。
20 个防御式 CSS 开发经验 - 图3
HTML的结构设计是基本前提,避免“表格思维”,避免多余的行 / 列元素(过度包装元素)。
如,这样设计:

  1. <div class="overview-content">
  2. <div class="sky-card overview-card">...</div>
  3. <div class="sky-card overview-card security-defense-card">...</div>
  4. <div class="sky-card overview-card">...</div>
  5. <div class="sky-card overview-card">...</div>
  6. </div>

1-2. 不要用JavaScript控制布局

线上问题:
20 个防御式 CSS 开发经验 - 图4
用CSS实现同样的效果:
20 个防御式 CSS 开发经验 - 图5

2、避免用float / position: absolute / display: table等方式布局

所有布局和对齐需求,无一例外用 Flexbox / Grids 实现。

3、避免定高/定宽,用min-width/min-height替代

固定宽/高最容易出现的问题是内容溢出。没必要通过定宽高对齐,可以利用Flexbox的位伸/收缩特性。可以定义最小宽/高。

4、避免侵入性(损人利自己)的写法

  • 避免影响全局样式,如:* { ... }:root {...}div { ....}等。
  • 避免影响通用组件样式,如:.next-card {...},如果要定制单加一个class名。
  • 不要直接修改全局CSS变量,把自己的CSS变量定义在模块的范围内。
  • 不要写z-index:999。一般1~9,防止被遮挡10~99,绝对够用了。
  • 不要在标签上定义style属性。不要在JS代码中做样式微调,这样今后无法统一升级CSS样式。
  • 只有完全不可修改的样式才能用!important,利用选择器优先级调整样式。

    5、避免CSS代码的误改 / 漏改

  • 将选择器拆开写,如.card-a, .card-b { ... },写时方便,改时难,修改时容易影响其它元素,不如分开写(除非像css reset这种特别确定的情况)。

  • 将样式集中在一起,容易改错。保持CSS代码和文件在相应的层级上,同一模块的放一起。避免混入通用样式中,为了避免改错,允许适当冗余。
  • @media时,会集中覆写一批元素的样式,更新样式时非常容易遗漏。所以必须拆开写,和对应模块的样式放在一起。不要集中放在文件底部,或是集中放在某一个文件里。
  • 及时清除“死代码”。
  • 定义样式要写全,微调样式要写具体,如: ```css .mod { margin: 0; }

/ 其它地方需要微调时 / .biz-card .mod { margin-bottom: 16px; }

  1. <a name="f1kH9"></a>
  2. ## 6、避免CSS样式冲突
  3. - 限定作用范围。如,`.my-module .xxx { ... }`。
  4. - 业务代码中的样式要加前缀,或借鉴BEM命名方式。如:`.overview-card-title { ... }`。用CSS Module也可以。
  5. - 注意选择器的精确性。级层过长过于复杂的CSS选择器会影响性能,但要注意:有时需要精确选择某些元素,如仅选择一级子元素,`.overview-card-content > .item { ... }`。
  6. <a name="XfOsu"></a>
  7. ## 7、防止内容不对齐
  8. 受字体、行高等因素影响(如图),用Flexbox实现对齐最可靠:
  9. - height / line-height 不可靠。
  10. - display:inline-block / vertical-align:middle 不可靠。
  11. ![](https://cdn.nlark.com/yuque/0/2022/webp/396745/1642212061462-db89edb3-5cb9-4e8e-93af-79c3adb2d847.webp#clientId=u75386ffd-f5ea-4&from=paste&id=u8b22d857&originHeight=308&originWidth=1072&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=shadow&taskId=ud1df7b70-5418-4838-90b8-f5f42cf483c&title=)![](https://cdn.nlark.com/yuque/0/2022/webp/396745/1642212061471-835233b8-f83a-45fa-ba11-4b2fdddacceb.webp#clientId=u75386ffd-f5ea-4&from=paste&id=u558defa0&originHeight=192&originWidth=606&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=shadow&taskId=ub587a736-4efb-4bb9-aa51-2e0a9f02589&title=)<br />用Flexbox实现对齐<br />![](https://cdn.nlark.com/yuque/0/2022/webp/396745/1642212061426-a11330ae-6a15-4c53-b85c-e64ea9277d17.webp#clientId=u75386ffd-f5ea-4&from=paste&id=u2e01bca4&originHeight=232&originWidth=954&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=shadow&taskId=uc5aa6719-4799-498e-a825-b855709b20b&title=)![](https://cdn.nlark.com/yuque/0/2022/webp/396745/1642212061400-b1477923-2bb6-4b70-b605-bc66e9e2f02d.webp#clientId=u75386ffd-f5ea-4&from=paste&id=ua54ccab5&originHeight=198&originWidth=550&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=shadow&taskId=u42757789-cf9f-4f51-ac35-074d53f2dae&title=)
  12. <a name="rP0kG"></a>
  13. ## 8、防止内容溢出
  14. 包括文字/图表等内容在宽度变化时或是英文版下容易出现溢出(如图)。<br />![](https://cdn.nlark.com/yuque/0/2022/webp/396745/1642212061870-01ac9077-aee9-4e41-b57b-4f599fb1fef8.webp#clientId=u75386ffd-f5ea-4&from=paste&id=u730df789&originHeight=484&originWidth=1080&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u44b6df19-2122-4b32-affa-f3105ccf244&title=)
  15. - 图表要支持自动 resize。
  16. - 图片要限制大小范围,如:max-width、max-height min(100px, 100%)、max(100px, 100%)
  17. 注意:min() / max() 兼容性:chrome 79+ / safari 11 / firefox 75<br />![](https://cdn.nlark.com/yuque/0/2022/webp/396745/1642212061941-ab13da77-1fb5-457d-9a85-64193441197f.webp#clientId=u75386ffd-f5ea-4&from=paste&id=u636ff22e&originHeight=384&originWidth=860&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u91f79fba-236d-45ec-9e13-23330ca87ee&title=)
  18. - 不能固定宽/高。(见规则3)
  19. - 不要在容器元素定义overflow:hidden
  20. <a name="jCrFv"></a>
  21. ## 9、防止内容过度拥挤
  22. 为了防止内容过长时紧帖到后面的内容,水平排列元素之间要设置间距,一般是8px。<br />![](https://cdn.nlark.com/yuque/0/2022/png/396745/1642212061814-02c5ae44-f9be-42f7-8cae-b2ae58f211da.png#clientId=u75386ffd-f5ea-4&from=paste&id=uc81fa487&originHeight=138&originWidth=1080&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=shadow&taskId=u2143353c-74b7-47d3-ad1d-fb30039aecb&title=)<br />如果用flexbox要加上gap。考虑到gap的兼容性:chrome 84,稳定起见用margin。<br />![](https://cdn.nlark.com/yuque/0/2022/webp/396745/1642212061967-b9dbf301-ba2b-4d23-a641-90d70392f59f.webp#clientId=u75386ffd-f5ea-4&from=paste&id=ue27183a1&originHeight=129&originWidth=1080&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=shadow&taskId=uea4a6cd4-1409-4c12-b296-5a2541b3003&title=)
  23. ```css
  24. .item {
  25. margin: 0 8px 0 0;
  26. }
  27. .item:last-child {
  28. margin-right: 0;
  29. }

10、防止内容被遮挡

定义负值时(负margin / top / left),小心内容被遮挡,避免这么定义。定义margin统一朝一个方向,向下和向右定义,再重置一下:last-childposition: relative 平时很常用,发生遮挡时会造成链接无法点击(如图)。
20 个防御式 CSS 开发经验 - 图6

11、防止可点击区域过小

小于32x32像素的可点击元素,通过下面的方式扩大可点击区域:
20 个防御式 CSS 开发经验 - 图7

  1. .btn-text {
  2. position: relative;
  3. }
  4. /* 比 padding 副作用小 */
  5. .btn-text::before {
  6. content: '';
  7. position: absolute;
  8. top: -6px;
  9. left: -8px;
  10. right: -8px;
  11. bottom: -6px;
  12. }

12、防止内容显示不全 / 被截断

  • 在定义overflow:hidden时,就要考虑内容是否有被截断的可能。一般不要加在容器元素上。
  • 防止长文字被生生截断(如图),加省略号。

线上问题

  1. overflow: hidden;
  2. text-overflow: ellipsis;
  3. white-space: nowrap;

不想折行,溢出加省略号,这都没问题。但忽略了对inline元素无效,所以要再加一条display: block

13、防止该折行不折行 / 不该折行的折行

首先必须理解UI,有3种情况:哪些需要折行,哪些不能折行,哪些不能从中间断行。

  1. 大部分情况需要折行,不能为了保持UI美观而损失内容的完整性。

一般用overflow-wrap,尽量不要用word-wrap(不符CSS标准):overflow-wrap: break-word配合overflow-wrap,可再加上hyphens: auto(目前兼容性不够)
限定多行:-webkit-line-clamp: 3

  1. 不能折行,如标题 / 列头 / 按钮等。开发中要理解内容,哪些元素不应该折行。

20 个防御式 CSS 开发经验 - 图9

  1. overflow: hidden;
  2. text-overflow: ellipsis;
  3. white-space: nowrap;

表格列数过多(>5列)时,会要求锁列,此时,th 定义 white-space: nowrap 强制不折行

  1. 不能从中间断行的情况(如图)

20 个防御式 CSS 开发经验 - 图10

14、防止滚动链问题

浮层的场景下需要避免滚动链问题:子元素可滚动,如果父元素也有滚动区域,在子元素上滚动时,触顶/触底后,会影响父元素滚动。关掉浮层后,用户会发现页面滚到了其它位置。

优化前 优化后
20 个防御式 CSS 开发经验 - 图11 20 个防御式 CSS 开发经验 - 图12

|

  1. overscroll-behavior: contain;
  2. overflow-y: auto;
  3. overflow-x: hidden;

注意:避免出现同时出现水平/垂直滚动条 兼容性:chrome 63+ / firefox 59+ / safari和edge不支持

15、防止图片变形

图片被置于特定比例的容器中时,固定宽/高和约束最大宽/高,都可能会导致图片变形。
20 个防御式 CSS 开发经验 - 图13

  1. .head img {
  2. width: 100%;
  3. height: 100%;
  4. }

20 个防御式 CSS 开发经验 - 图14

  1. .head img {
  2. width: 100%;
  3. height: 100%;
  4. object-fit: cover;
  5. }

在Flexbox容器内,图片高度会被自动拉伸。因为不定义align-items,默认是stretch。
20 个防御式 CSS 开发经验 - 图15
20 个防御式 CSS 开发经验 - 图16

16、防止图片加载失败

需要考虑图片加载慢或加载失败的情景。在图片的容器上加边或加底色。
20 个防御式 CSS 开发经验 - 图1720 个防御式 CSS 开发经验 - 图18

  1. .head {
  2. background: #eee;
  3. box-shadow: inset 0 0 0 1px #aaa;
  4. }

17、防止CSS变量未引入

在标准化开发中,提倡使用全局的CSS变量。业务代码中,利用CSS变量也可以方便的进行全局的控制。在使用CSS变量时要加上缺省值。

  1. font-size: var(--tab-item-text-size-s, 12px);

18、防止CSS兼容性问题

  • 不要加浏览器厂商前缀,让CSS预编译自动处理,像-webkit--moz-
  • 不要用仅特定浏览器厂商支持的属性。

    19、Flexbox常见防御性写法

    Flexbox的默认表现比较多,不能简单的定义display:flex,或是flex:1
  1. Flexbox容器元素通常要做如下定义:要支持多行(默认是单行),交叉轴上垂直居中(默认是stretch),主轴上采用space-between,将自由空间分配到相邻元素之间。

    1. display: flex;
    2. flex-wrap: wrap;
    3. justify-content: space-between;
    4. align-items: center;
  2. Flexbox的盒子元素,不要固定宽/高,更不要指定百分比的值。Flexbox会自动拉伸/收缩盒子元素,能够精确到浮点数,指定具体值会破坏原本的“弹性”。

  3. Flexbox的盒子元素要定义间距。

案例分析:
20 个防御式 CSS 开发经验 - 图19
优化后:
20 个防御式 CSS 开发经验 - 图20
参考代码,总结用法:

  1. .new-overview-v2-center-box .left .top {
  2. /* position: relative; */
  3. display: flex;
  4. flex-wrap: wrap;
  5. align-items: center;
  6. justify-content: space-between;
  7. /* height: 100%; */
  8. }
  9. .new-overview-v2-center-box .left .top .item {
  10. /* width: 25%; */
  11. flex: 1;
  12. min-width: max-content;
  13. margin: 0 8px 8px 0;
  14. }

20、Grid常见防御性写法

  • 不固定网格的宽度,用minmax(最小值,1fr)。
  • 定义间距grid-gap: 8px。
  • 不固定列数, 利用auto-fit / auto-fill自动适配(如图)。

20 个防御式 CSS 开发经验 - 图21

  1. .wrapper {
  2. display: grid;
  3. grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  4. grid-gap: 8px;
  5. }
  6. .item {
  7. border: 2px solid #aaa;
  8. box-sizing: border-box;
  9. min-height: 128px;
  10. }