有意思的CSS

读《CSS揭秘》笔记。作者 LEA VEROU。原书有很多有意思的CSS效果,这里记录一些比较实用而且具有代表性的效果。如果想了解更多页面css的效果实现,可以去读一读原书,还是很不错的。

1.currentColor

CSS居然有变量?没想到吧。

在 CSS 颜色(第三版)(http://w3.org/TR/css3-color)规范中,增加了很多新的颜色关键字,比如 lightgoldenrodyellow等,其实并不是很常用。但是,我们还得到了一个特殊的颜色关键字 currentColor,它是从SVG那里借鉴来的。这个关键字并没有绑定到一个固定的颜色值,而是一直被解析为color。实际上,这个特性让它成为了CSS中有史以来的第一个变量。虽然功能很有限,但它真的是个变量。

举个例子,假设我们想让所有的水平分割线(所有

元素)自动与
1
2
3
4
5
6
7
文本的颜色保持一致。有了 currentColor 之后,我们只需要这样写:

```css
hr {
height: .5em;
background: currentColor;
}

如果你没有给边框指定颜色,它就会自动地从文本颜色那里得到颜色。这是因为 currentColor 本身就是很多 CSS 颜色属性的初始值,比如border-color 和 outline-color ,以及 text-shadow 和 box-shadow 的颜色值,等等

2. 响应式布局

尤其常见于各类网站主页,宣传页面,需要展示的信息适量,也不需要大量的用户操作。

建议尽量少的使用媒体查询,不然修改起来绝对是噩梦。

比较常见的实践是用多种分辨率来测试一个网站,然后添加越来越多的媒体查询(Media Query)规则来修补网站在这些分辨率下出现的问题。然而对于今后的CSS改动来说,每个媒体查询都会增加成本,而这种成本是不应轻易上升的。未来每次对CSS代码的修改都要求我们逐一核对这些媒体查询是否需要配合修改,甚至可能要求我们反过来修改这些媒体查询的设置。这一点常常被我们忽略,后患无穷。你添加的媒体查询越多,你的CSS代码就会变得越来越经不起折腾。

下面还有一些建议,可能会帮你避免不必要的媒体查询。

使用百分比长度来取代固定长度。如果实在做不到这一点,也应该
尝试使用与视口相关的单位( vw 、 vh 、 vmin 和 vmax ),它们的值解
析为视口宽度或高度的百分比。

当你需要在较大分辨率下得到固定宽度时,使用 max-width 而不是
width ,因为它可以适应较小的分辨率,而无需使用媒体查询。

不要忘记为替换元素(比如 img 、 object 、 video 、 iframe 等)设
置一个 max-width ,值为 100% 。

假如背景图片需要完整地铺满一个容器,不管容器的尺寸如何变化,
background-size: cover 这个属性都可以做到。但是,我们也要时
刻牢记——带宽并不是无限的,因此在移动网页中通过 CSS 把一张
大图缩小显示往往是不太明智的。

当图片(或其他元素)以行列式进行布局时,让视口的宽度来决定
列的数量。弹性盒布局(即 Flexbox)或者 display: inline-block
加上常规的文本折行行为,都可以实现这一点。

在使用多列文本时,指定 column-width (列宽)而不是指定
column-count (列数),这样它就可以在较小的屏幕上自动显示为单
列布局。

我们的思路是尽最大努力实现弹性可伸缩的布局,并在媒体查询的各个断点区间内指定相应的尺寸。

3.合理使用简写

大部分时候使用简写可能会误导我们,比如编程的时候使用不常见的简写作为变量命名,但是在CSS中,一定程度上还是推荐使用简写的。

1
2
background: rebeccapurple;
background-color: rebeccapurple;

前者是简写,它可以确保你得到 rebeccapurple 纯色背景;但如果你用的是展开式的单个属性(background-color ),那这个元素的背景最终有可能会显示为一个粉色的渐变图案、一张猫的图片或其他任何东西,因为同时可能会有一条 background-image 声明在起作用。在使用展开式属性的写法时,通常会遇到这样的问题:展开式写法并不会帮助你清空所有相关的其他属性,从而可能会干扰你想要达到的效果。

当然,除非你是像用这个背景色作为缺失图片或者元素时候的安全色。这样写也未尝不可。

4. 透明边框

假设我们要为一个容器实现透明的边框,可能会这样写:

1
2
border: 10px solid hsla(0,0%,100%,.5);
background: white;

题外话:关于HSLA,可以理解为和rgba()相似的生成颜色的方法。

hsla() 函数使用色相、饱和度、亮度、透明度来定义颜色。

但结果是容器是看不到透明边框的。

image

事实是,透明边框是确实存在的,但为什么我们看不到呢?原因很简单,是因为背景与border是重叠的。默认情况下,背景会延伸到边框所在的区域下层。

image

上图中,绿色的为背景色,棕色的为边框。

我们可以通过 background-clip属性来调整上述默认行为所带来的不便。这个属性的初始值是 border-box ,意味着背景会被元素的border box(边框的外沿框)裁切掉。如果不希望背景侵入边框所在的范围,我们要做的就是把它的值设为 padding-box ,这样浏览器就会用内边距的外沿来把背景裁切掉

正确的显示出透明边框:

1
2
3
border: 10px solid hsla(0,0%,100%,.5);
background: white;
background-clip: padding-box;

image

5.calc方法

1
2
3
div{
width: calc(100% - 100px);
}

calc() 函数用于动态计算长度值,但是内部的表达式仅支持加减乘除的运算。

请不要忘记在 calc() 函数内部的 - 和 + 运算符的两侧各加一个空白符,否则会产生解析错误!这个规则如此怪异,是为了向前兼容:未来,在 calc() 内部可能会允许使用关键字,而这些关键字可能会包含连字符(即减号)。

6.CSS变形-平行四边形

用CSS做背景是很常见的需求,除了矩形,圆角矩形以外,常用的其他几何图形,也是可以用CSS直接画出来的。这里看看平行四边形。

1
transform: skewX(-45deg);

使用斜向变形是最常用的解决方案。

image

变形之后,容器里面的文本也会发生倾斜:

image

这肯定不是我们想要得到的效果。解决的办法如下,在容器里再添加一个容器,让文本和容器反向斜向变形,将里面的文字掰正。

1
2
3
4
5
<a href="#yolo" class="button">
<div>Click me</div>
</a>
.button { transform: skewX(-45deg); }
.button > div { transform: skewX(45deg); }

这样确实成功实现了我们的需求。但是有没有更方便的方式呢?

1
2
3
4
5
6
7
8
9
10
11
12
13

.button {
position: relative;
/* 其他的文字颜色、内边距等样式…… */
}
.button::before {
content: ''; /* 用伪元素来生成一个矩形 */
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
z-index: -1;
background: #58a;
transform: skew(45deg);
}

另一种思路是把所有样式(背景、边框等)应用到伪元素上,然后再对伪元素进行变形。因为我们的内容并不是包含在伪元素里的,所以内容并不会受到变形的影响。

我们希望伪元素保持良好的灵活性,可以自动继承其宿主元素的尺寸,甚至当宿主元素的尺寸是由其内容来决定时仍然如此。一个简单的办法是给宿主元素应用 position: relative 样式,并为伪元素设置 position:absolute ,然后再把所有偏移量设置为零,以便让它在水平和垂直方向上都被拉伸至宿主元素的尺寸。

此时,用伪元素生成的方块是重叠在内容之上的,一旦给它设置背景,就会遮住内容。为了修复这个问题,我们可以给伪元素设置z-index:-1样式,这样它的堆叠层次就会被推到宿主元素之后。

7.毛玻璃效果

image

毛玻璃背景也是我们很常用的一种效果,在web上要实现这样的效果同样可以采用之前伪类的思路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

body, main::before {
background: url("tiger.jpg") 0 / cover fixed;
}
main {
position: relative;
background: hsla(0,0%,100%,.3);
overflow: hidden;
}
main::before {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
filter: blur(20px);
margin: -30px;
}

8.元素扩大点击区域

能让用户舒服的点击页面上的元素是用户体验提升非常重要的一部分。尤其是在面对内容很多,按钮很小的时候,扩充元素的点击区域是一个很好的解决方案。

如果对用户体验感兴趣,那你很可能听说过 Fitts 法则。它是由美国心理学家 Paul Fitts 于 1954 年首次提出的。Fitts法则认为,人类移动到某个目标区域所需的最短时间是由目标距离与目标宽度之比所构成的对数函数。如果要用数学公式把它表达出来,通常就是:

image

T 表示所需时间,D 是从起点到目标中心的距离,W 是目标区域的宽度,而 a 和 b都是常数。

image

扩张热区最简单的办法是为它设置一圈透明边框,因为鼠标对元素边框的交互也会触发鼠标事件,这一点是描边和投影所不及的。就这个例子而言,把元素的热区在四个方向上各向外扩大 10px 其实很容易做到。

1
2
3
4
5
6
7
8
9
10
button {
position: relative;
}

button::before {
content: '';
position: absolute;
top: -10px; right: -10px;
bottom: -10px; left: -10px;
}

上面使用的伪元素可以影响到用户鼠标点击的交互。

9.元素居中

当宽度确定,水平居中的常用方式(flex布局除外):

1
2
3
4
.content{
width:100px;
margin:auto;
}

现代写法:

1
2
3
4
.content{
width:100px;
margin:0 calc(50% - 50px);
}

垂直居中呢?当高度确定的时候可以这样来:

1
2
3
4
5
6
7
8
9
main {
position: absolute;
top: 50%;
left: 50%;
margin-top: -3em; /* 6/2 = 3 */
margin-left: -9em; /* 18/2 = 9 */
width: 18em;
height: 6em;
}

这段代码在本质上做了这样几件事情:先把这个元素的左上角放置在视口(或最近的、具有定位属性的祖先元素)的正中心,然后再利用负外边距把它向左、向上移动(移动距离相当于它自身宽高的一半),从而把元素的正中心放置在视口的正中心。

换个写法:

1
2
3
4
5
6
7
main {
position: absolute;
top: calc(50% - 3em);
left: calc(50% - 9em);
width: 18em;
height: 6em;
}

高度不确定的时候呢?

CSS 领域有一个很常见的现象,真正的解决方案往往来自于我们最意想不到的地方。在这个例子中,答案来自于 CSS变形属性。当我们在translate()变形函数中使用百分比值时,是以这个元素自身的宽度和高度为基准进行换算和移动的,而这正是我们所需要的。接下来,只要换用基于百分比的 CSS 变形来对元素进行偏移,就不需要在偏移量中把元素的尺寸写死了。这样我们就可以彻底解除对固定尺寸的依赖:

1
2
3
4
5
6
main {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

没想到吧,解决不确定高度的垂直居中,居然使用transform这个位移属性……

最后放上flex的解决方式,这个可就简单多了:

1
2
3
4
5
6
7
main {
display: flex;
align-items: center;
justify-content: center;
width: 18em;
height: 10em;
}