防御性CSS介绍

157 阅读13分钟

我们经常希望有方法能避免CSS问题和表现行为不一致的发生,大家都知道,内容永远是动态的,网页永远会变化。因此这种变化带来的CSS问题和表现行为不一致。

此文章由我翻译,原文出自 ishadeed.com/article/def…

也会结合实际项目中用到的例子加以阐明

感谢作者Ahmad的付出。

防御性CSS其实是一种代码片段集合,它可以帮助你书写CSS的时候避免各种错误。换句话说,你使用防御性CSS会降低问题出现的几率。

  • flexbox换行
  • 空间
  • 长内容
  • 防止图片拉伸或者压缩
  • 锁定滚动条联动
  • CSS函数变量替代
  • 使用固定的高度和宽度的情况
  • 忘记背景重复吧
  • 垂直媒体查询
  • 使用justify-content: space-between的时候
  • 图片上的文字处理
  • 当需要的时候再显示滚动条
  • 滚动条槽
  • 防止图片拉伸或者压缩
  • Flex布局中的最小内容尺寸限制
  • 栅格布局中的最小内容尺寸限制
  • Auto Fit VS Auto Fill(自动适应对自动填充)
  • 图片最大宽度
  • Position:Sticky css栅格
  • 分组选择器

Flexbox换行

现在来说,flex算是现在CSS布局结构用得最多的了,简单添加display flex给容器,就可以让子元素都顺序整齐布局。

当容器没有足够空间时候,子元素默认是不会换行的。我们需要添加flex-wrap: wrap

举个例子,我们都做过这种结构:

.options-list {     
    display: flex;
}

但是当空间并不充足的时候,滚动条也会出现,我们需要添加换行属性让他换行修正。

.options-list {     
    display: flex;     
    flex-wrap: wrap;
}

使用Flex布局的一个基础操作就是你需要允许换行,除非你确实需要横向布局需要一个滚动条。这就另当别论了,但是使用flex-wrap避免窗口或者大小不统一导致的布局变化是很有必要的。

空间

我们UI开发需要处理不同的文字长度,这意味着足够的多余空间需要一起考虑进组件去,虽然有时候看起来很多余。

在这个例子中,我们有一个左侧模块标题和右侧可点击的按钮,现在看起来还可以吧,但是当标题文字超长的时候会发生什么事情呢。

发现了吗,按钮已经距离文字很近了,你是不是开始想要使用多行换行了,先把这个放一边我们来说说这个空间。如果这个标题我们给了一定的空间,就不会看到这个问题了。

.section__title {     
  margin-right: 1rem;
  overflow: hidden;
  text-overflow: ellipsis;    
  white-space: nowrap;
  word-break: break-all;
  width: xxxpx;
}

长内容 适配长内容是构建css结构比较重要的一点,你也许在之前的情况下看到过,因为标题过长而导致可显示的内容缩短和裁剪。虽然这个是可选的,但是部分场景下UI还是需要考虑到

对于我来说,这应该算一种防御性CSS,有什么比得上修复还没发生的问题更让人高兴的呢

比如下面这种设计稿,他看起来很不错对吧。

可是呢,这个内容其实是用户填写编辑的,我们要非常小心留意如果防御结构变化比如说太长了。

在这个情况下,统一性就显得格外重要了。我们可以简单的缩小内容展示,使用text-overflow来达到目的

.username {     
    white-space: nowrap;     
    overflow: hidden;     
    text-overflow: ellipsis;
}

如果你对长内容的兼容有兴趣,这里还有一篇文章讲述如何处理长文章的样式问题

防止图片拉伸或者压缩 当我们无法控制一个图片的横纵比例时,最好想一想如何防止用户上传图片和设计稿的图片横纵比不一致问题。

下面的例子我们展示一个卡片组件,他看起来不错对吧

当用户上传一个不同尺寸的图片,他会被撑大,这就不好了,看看我们不同图片被撑大是什么样的

简单的修复方法就是添加一个样式,object-fix

.card__thumb {
    object-fit: cover;
}

项目中,我更倾向于为所有图片标签

img {
    object-fit: cover;
}

锁定滚动条联动 你有没有经历过这种情况,当你打开一个弹窗开始滚动弹窗的滚动条时,弹窗滚动条到底了,整体的页面就跟着一起滚动了,这就叫滚动条联动。

在之前几年是有几种方法可以防止这种行为表现,但是现在我们其实可以加css就能防止这种事情发生了。感谢overscroll-behavior这个css属性

下面的图片可以看到这种情况一般是什么样的

为了避免这种情况,我们可以通过添加这个属性给任何一个需要滚动的组件,比如聊天床,小菜单等。这个属性优势在于你无需担心任何影响,除非有滚动出现。

.modal__content {
    overscroll-behavior-y: contain;
    overflow-y: auto;
}

CSS函数变量替代 css函数现在越来越获得web设计青睐,我们可以接受这样一个不会破坏用户体验的方法,以防止CSS变量为空。

这对于CSS变量要通过Javascript赋予数值来说十分好用。比如:

.message__bubble {
    max-width: calc(100% - var(--actions-width));
}

这个变量--actions-width是

.message__bubble {
    max-width: calc(100% - var(--actions-width, 70px));
}

当变量还没有赋值的时候,这个地方就可以用70px占位而不至于出错。这个方法可以用来给js赋值出错的情况下撑一下场面

使用固定的高度和宽度的情况 有一种非常非常常见的情况就是,结构上宽高外漏的情况,这导致了内部高度过高超出边界区域的问题。

固定高度 我经常看到有些部分会固定高度,但是内部核心区域却超过了外框,这样会导致样式上的结构错位,想不出来什么样子吗,下面就是例子

.hero {
    height: 350px;
}

为了规避这个风险,我们需要把height更换为min-height

.hero {
    min-height: 350px;
}

这样如果内容变大就可以超出滚动了,结构不会出问题。

固定宽度 有没有看过一个按钮里面的文字和边边过于贴近?因为这些按钮宽度固定了

.button {
    width: 100px;
}

如果按钮内容超过100px,文字就会非常贴边,如果太长了就会超出边界,这不好看啊

我们为了修复这个问题,我们可以简单把width设置为min-width

.button {
    min-width: 100px;
}

忘记背景重复吧 我们经常在使用图片作为背景的时候,我们倾向于忘记更宽更大的屏幕下显示的情况,这种情况下图片背景会重复。

这种情况笔记本上不会显示,但是大屏幕显示器会经常出现

避免这种情况的好方法是为background-repeat设置no-repeat

垂直媒体查询 有些时候,你测试ui可行性的时候仅仅是拉伸浏览器宽度而已,不同浏览器高度测试总是会被忽略,而这些问题也要进行面对。

这里是一个我见过好几次的问题,我们有一个侧边栏组件和一个中心区域。侧边栏里面的连接应该放在侧边最底下。

看一下这个例子,现在这个链接还看起来ok是吧,但是我看到的连接里面,开发者使用了position:sticky属性,让侧边栏一直保持右侧模式。这样就可以让链接一直在下面。

但是呢,当浏览器高度变小的时候,这就会错位了,注意到了吗,这里面的两个链接已经重叠在一起了。

通过使用垂直媒体查询,我们可以简单避免这个问题出现

@media (min-height: 600px) {
    .aside__secondary {
        position: sticky;
        bottom: 0;
    }
}

这样呢,只有当浏览器高度大于等于600px的时候,底部链接才会粘在底部。是不是好点了?

也许会有更好的方法来处理这个问题,但我现在只是想解释垂直高度媒体查询的适配作用。

如果我想要解释如何去使用垂直媒体查询,我可能要写一篇长篇大论了。ishadeed.com/article/res…

使用justify-content: space-between的时候 在flex布局结构中,你应该用过justify-content属性来

当我们有四个子元素的时候,这中间的空隙不是gap或者margin属性实现的,是justify-content:space-bewteen自动排列的

.wrapper {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
}

当子元素减少的时候,就会变成这样

这样就不太好看了,当然我们也有很多方法处理这个问题

外边距margin

flex盒子空隙 flexbox gap

内间距padding (可以通过父级别选择器控制子元素)

为空白处添加空元素

对我来说会选择gap属性

.wrapper {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}

图片上的文字处理 当使用文字覆盖到图片上的时候,需要考虑到当图片加载失败的时候,文字会因为背景颜色看不清楚。

比如说这样

这样文字还看得清楚,但是图片失效的话呢

我们简单的给img标签一个修正图片背景颜色的属性即可,这个背景色只会在图片加载失败的时候生效,是不是很酷啊

.card__img {
    background-color: grey;
}

注意固定栅格的尺寸

当我们有一个栅格包好了主要元素和侧边,这个样式大概是这样

.wrapper {
    display: grid;
    grid-template-columns: 250px 1fr;
    gap: 1rem;
}

这会因为缺少空间而破坏小尺寸屏幕显示情况。为了避免这种事情发生,请你警察使用媒体查询来编写css栅格,像这样

@media (min-width: 600px) {
    .wrapper {
        display: grid;
        grid-template-columns: 250px 1fr;
        gap: 1rem;
    }
}

当需要的时候再显示滚动条 幸运的,我们可以自己控制滚动条的显示或者隐藏以防止长内容看不到。这里我强烈推荐给overflow使用auto属性而不是scroll

比如说这样

注意到了吗,不管是内容多短,还是会有滚动条。UI上说这并不好看,当作为一个设计师,看到这个进度条总是出现会觉得非常奇怪。

.element {
    overflow-y: auto;
}

通过使用overflow-y: auto, 滚动条仅仅会显示在长内容情况,而短内容不会出现。下面例子就是这样

滚动条槽 另一个关于滚动条的东西就是滚动条槽,对于刚才的例子,当内容过长的时候,滚动条的添加会导致内容偏移。因为结构上要给滚动条的空间留出位置。

比如这样

注意到了吗,看到内容变长后因为滚动条导致的偏移了吧,我们可以通过设置scrollbar-gutter属性来修正这个问题

.element {
    scrollbar-gutter: stable;
}

滚动条不再占用影响结构

Flex布局中的最小内容尺寸限制 如果flex元素拥有文字内容或者有一个大一些的图片让内容过长,浏览器是不会缩放的,因为flex布局的默认表现是如此

比如下面的例子

.card {
    display: flex;
}

当标题文字特别多的时候,他不会换行

即使你使用overflow-wrap:break-word 他也不行

.card__title {
    overflow-wrap: break-word;
}

为了改变这个默认样式,我们需要设置flex元素的最小宽度为0,因为默认元素的最小宽度是auto,overflow样式就会不生效

.card__title {
    overflow-wrap: break-word;
    min-width: 0;
}

这种情况也适用于column排列的flex结构换行,当然这个情况我们要设置min-height为0

栅格布局中的最小内容尺寸限制 与flex结构差不多,CSS栅格有一套子元素默认的最小尺寸限制,那就是auto。这意味着当子元素过于大的时候,会超出边界

在这个例子中。我们有一个滚动跑马灯结构,他们大概是这样写出来的

<div class="wrapper">
    <main>
        <section class="carousel"></section>
    </main>
    <aside></aside>
</div>
@media (min-width: 1020px) {
    .wrapper {
        display: grid;
        grid-template-columns: 1fr 248px;
        grid-gap: 40px;
    }
}
.carousel {
    display: flex;
    overflow-x: auto;
}

因为这个跑马灯结构是flex属性设置也不会换行,这个宽度已经超出了主要区域,因此栅格元素也遵循了这一点一起超出了主要区域,所以这里需要水平滚动。

为了修正这个问题,我们有三种不同解决方案

使用minmax()

为栅格元素设置min-width

设置overflow:hidden给栅格元素

作为一个防御性CSS推动者,我倾向于使用minmax()函数来解决这个问题


@media (min-width: 1020px) {
    .wrapper {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 248px;
        grid-gap: 40px;
    }
}

实际例子中我们用于处理横排图标十分有效

推荐文章 “Preventing a Grid Blowout” 和 “You want minmax(10px, 1fr) not 1fr” 作者也是Chris Coyier

Auto Fit VS Auto Fill(自动适应对自动填充) 当使用CSS栅格函数minmax()的时候,选择auto-fix还是auto-fill就显得格外重要。当你使用错误的时候,它会引发意料之外的问题。

当使用minmax()函数的时候,auto-fit会扩展栅格元素来填充剩下的区域。auto-fill会保留空余的区域而不会填充剩余部分

这就是说,auto-fix的使用会导致元素太宽,尤其是意料之外的时候,比如说

.wrapper {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    grid-gap: 1rem;
}

如果这个栅格只有一个元素,而你用了auto-fix,那这个元素会被撑开特别大,变成和容器宽度一样大

大多数情况下,我们不需要这样的样式表现,所以使用auto-fill我觉得更好一些

.wrapper {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    grid-gap: 1rem;
}

图片最大宽度 通常标准上,不要忘记给所有图片设置最大宽度max-width:100%,你可以把这个属性放到reset.css中以便使用

img {
    max-width: 100%;
    object-fit: cover;
}

Position:Sticky css栅格 你有没有为栅格结构的子元素写过position:sticky?默认样式会导致这个元素会被拉伸。因为这样下面的例子导致整体元素拉伸十分大

解决这个问题你只需要重制align-self属性即可

aside {
    align-self: start;
    position: sticky;
    top: 1rem;
}

分组选择器 其实我们并不建议你使用分组选择器,不同浏览器对于他们的支持是不一样的。比如说input的placeholder需要跨浏览器适配添加不同的前缀。当我们使用分组选择器,所有规则都会失效,参见w3c官方指引

/* 求你不要这样做 */
input::-webkit-input-placeholder,
input:-moz-placeholder {
    color: #222;
}

请你这样做

input::-webkit-input-placeholder {
    color: #222;
}

input:-moz-placeholder {
    color: #222;
}