平时写样式的时候,经常碰到一个头疼事儿:想把一个CSS属性的值,设成另一个属性的当前值,但那个值吧,可能自己也不知道是多少,或者后面还会变。比如想让圆角跟按钮的高度挂钩,高度要是变了,圆角也跟着变。可惜CSS里目前还没个直接能“抄”别人家值的函数。
摘要
CSS里想直接引用另一个属性的值(比如把圆角设成高度的一半),目前没有现成的compute()函数可用。但别急,有好几种变通办法:用自定义属性存值再计算,这是最稳的;用还没浏览器支持的inherit()函数(但未来可期);用aspect-ratio让宽高互相关联;用currentColor这种关键词复用颜色;还有lh、cqh这类CSS单位也能间接拿到参照值。每种方法都有各自的适用场景,选对了就能搞定。
自定义属性,给值起个“小名”
这招最实在,也最靠谱。想用某个值,那就先给它声明一个自定义属性(也叫CSS变量),存起来,用到哪儿都行。
button {
--btn-height: 3rem;
height: var(--btn-height);
border-radius: calc(var(--btn-height) * 0.3);
}这么一来,圆角就乖乖跟着高度走了。要是把--btn-height放在:root里,那整个页面都能用上,不过得记着,这玩意儿得先声明值才行,不然就抓瞎了。要是遇到高度是靠内边距撑起来的,那就得用calc算一下,比如:
header {
--header-padding: 1rem;
padding: var(--header-padding);
/* 高度靠内容撑,但咱能算出来 */
--header-height: calc(3rem + (var(--header-padding) * 2));
border-radius: calc(var(--header-height) * 0.3);
}这就像代数课,把未知数设成变量,然后一通算。唯一的小麻烦是,得提前知道或者能算出那个值,才能存起来。但要是那个值本身是动态的、由内容撑开的,这招就有点使不上劲儿了。
容器查询单位,偷看老爸的尺寸
这招专门对付那些想从父元素那儿“借”尺寸的情况。给父元素设个container-type,子元素就能用cqh(容器高度单位)这类单位了。
header {
height: 3rem;
container-type: size;
}
button {
height: 100%;
border-radius: calc(100cqh * 0.3);
padding-inline: calc(100cqh * 0.5);
}100cqh就是父元素高度的100%,也就是3rem。圆角和内边距都跟这个高度挂钩了。这里有个坑,container-type: size会让父元素失去内联方向上的自动尺寸,变得像块级元素一样撑满宽度,要是父元素宽度需要自适应,这招就不能乱用。而且它只能偷尺寸,颜色、圆角这些非尺寸属性就无能为力了。
宽高比例,一个顶俩
要是想实现“高度是宽度的一半”这种效果,aspect-ratio是绝配。它不直接告诉你宽度是多少,但它能让高度和宽度保持固定比例。
.box {
width: 30rem;
aspect-ratio: 2 / 1; /* 高度就是宽度的1/2,即15rem */
}这个比例关系是硬绑定的,改宽度,高度自动变。反过来,要是指定高度,宽度也会跟着变。用的时候得想清楚,到底谁是参照物。要是想用高度去定宽度,那就把高度写死,用aspect-ratio自动算出宽度。不过这招只对尺寸比例管用,别的属性别指望它。
currentColor,颜色的“抄作业”利器
currentColor就像个跟屁虫,它永远等于当前元素的color值。用它来设边框颜色、背景色啥的,特别方便。
button {
color: #e74c3c;
border: 2px solid currentColor;
background: hsl(from currentColor h s 90%);
}这么写,边框自动跟文字颜色一样,背景也是在文字颜色基础上调亮。要是color变了,所有用到currentColor的地方都跟着变,不用手动改一堆地方。但它的局限性也很明显,只能抄color,想抄背景色background-color?没门儿。不过社区已经有人提议搞个currentBackgroundColor了,要是真有了,那就更牛了,可以直接在背景色基础上调暗做边框色:border-color: hsl(from currentBackground h s calc(l - 30))。
字体相对单位,跟字号“锁死”
有些CSS单位天生就是参照某个字体属性的,比如lh(行高)、ex(x-height)、ch(0字符宽度)。用它们能让元素的尺寸跟文字排版紧密配合。
.card {
padding: 1lh; /* 内边距等于一行的高度 */
border-radius: 1ex; /* 圆角跟x-height挂钩 */
}最实用的要数lh,想要一个多行文本的容器高度正好等于行数乘以行高,height: 3lh就能搞定。不过得留神,要是用text-box这类属性修剪了行盒,lh单位算出来的就不准了。ex和ch适合做那种跟字体笔画宽度或字符宽度相关的微调,比如让输入框宽度正好能容下“用户名”三个字,用ch就很合适。
从字体文件“借”来的厚度
from-font这个关键词很特别,只用在text-decoration-thickness上。它让下划线的厚度由字体文件自己说了算。
a {
text-decoration-thickness: from-font;
}要是字体文件没提供这个信息,浏览器就会用默认的auto,通常就是1px。这玩意儿不太可控,因为不知道字体设计师到底给没给值。这时候可以用百分比替代,比如text-decoration-thickness: 10%,让下划线厚度随字号变化,虽然没那么精准,但至少是自己能控制的,比固定1px强多了。
视口比例单位,跟窗口尺寸“同步”
vw和vh老生常谈了,但它们的亲戚vi和vb(逻辑视口单位)可能更实用。vi是内联方向上的视口尺寸,比如横向书写时就是宽度。
.hero {
font-size: calc(2rem + 2vi); /* 字体大小随视口内联尺寸变化 */
padding-block: 5vh;
}用这些单位做响应式排版很顺手,但得注意,滚动条会占用视口尺寸,导致100vw可能比想象中宽,出现横向滚动条。解决方案是width: 100%结合max-width: 100vw,别一根筋用vw。
