最近苹果家的 Safari 浏览器悄咪咪更新到了 26 版本,本来嘛,浏览器发新版本就跟手机 App 更新一样稀松平常,一般就是修修补补加几个小功能。但这次还真有点不一样,官方博客里一翻,好家伙,新增了 75 个新特性,还有 171 项改进。这波操作简直可以称之为“史诗级”更新,尤其是 CSS 这一块,冒出了不少以前只能在 Chrome 里眼馋的功能。今天咱就盘一盘,这几个能实实在在解决开发痛点的新玩意儿到底怎么上手玩起来。
锚点定位
基础绑定
把两个在 HTML 里八竿子打不着的元素强行“粘”在一起,这事儿以前得靠复杂的 JavaScript 计算位置,或者用 CSS 的各种 hack 才能勉强实现。现在有了 CSS 锚点定位,这事儿就变得跟吃薯条蘸番茄酱一样简单。
咱们先弄个例子,页面上有两个 div,一个叫“锚点”,另一个叫“目标”,它俩在代码里是前后脚出现的兄弟关系。想让目标元素死死地贴在锚点元素的旁边,不管屏幕怎么缩放,它俩都像连体婴一样。
/* 给锚点元素起个独一无二的代号 */
.anchor-element {
anchor-name: --special-anchor;
}
/* 目标元素开始它的表演 */
.target-element {
position: absolute;
position-anchor: --special-anchor;
}只要给锚点元素写上 anchor-name 属性,起个以双减号开头的名字,再给目标元素写上 position-anchor 指向这个名字,目标元素就会立刻被“吸附”到锚点元素的正中央。即便这俩元素在 HTML 结构里隔了十万八千里,也丝毫不影响它俩的“感情”。
这里得提一嘴,虽然 CSS 让它们在视觉上绑定了,但在屏幕阅读器这类辅助技术眼里,它俩可能还是陌生人。所以最好用 aria-describedby 或者 aria-labelledby 给它们牵个线,这样用读屏软件的小伙伴才能明白这俩玩意儿是一伙的。
位置微调
光吸附到中心肯定不够,咱得把它挪到想要的位置,比如右上角。这时候 position-area 属性就派上用场了。
.target-element {
position: absolute;
position-anchor: --special-anchor;
position-area: top right;
}position-area 的工作原理特别像在锚点元素周围画了个九宫格,通过指定格子的名称,就能把目标元素扔到对应格子里。比如 top right,就是把目标元素放到锚点元素的右上角那一格。这个属性让元素的定位变得像玩游戏一样直观。
滚动驱动动画
阅读进度条
以前做个滚动条跟着页面走的效果,得监听滚动事件,计算滚动百分比,再动态修改元素的宽度,代码写起来又臭又长。现在用 scroll() 函数,几行 CSS 就能搞定一个丝滑的阅读进度条。
先定义一个简单的动画,让元素从宽度为 0 变到完整宽度。
@keyframes fillWidth {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
.progress-indicator {
transform-origin: left center;
animation: fillWidth linear;
}最关键的一步来了,把动画和滚动条绑定在一起。
.progress-indicator {
transform-origin: left center;
animation: fillWidth linear;
animation-timeline: scroll();
}加上 animation-timeline: scroll() 之后,这个动画就不再是按时间跑了,而是跟着页面滚动的进度走。页面滚到底,进度条动画也就刚好播放完,完全不需要 JavaScript 插手。而且 transform-origin: left center 保证了进度条是从左往右增长的,看着贼舒服。
视差入场
想让图片在滚动到可视区的时候,带着点淡入淡出或者上浮的效果,这效果以前得用 Intersection Observer 这类的 API 配合一堆逻辑才能实现。现在 view() 函数配合 animation-range 就能轻松搞定。
给所有图片绑一个淡入上浮的动画。
@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
img {
animation: fadeUp linear;
animation-timeline: view();
}这时候刷新页面会发现,动画在图片刚进入视野的时候就开始了,但还没播放完,图片可能就滚出屏幕了,效果大打折扣。这时候就需要 animation-range 来控制动画的播放区间。
img {
animation: fadeUp linear;
animation-timeline: view();
animation-range: 0% 50%;
}设置 animation-range: 0% 50%,意思就是当图片底部刚刚进入可视区底部时(0%),动画开始;当图片顶部滚动到可视区中间时(50%),动画结束。这样就能确保整个动画过程都在用户视线范围内完美呈现。animation-range 的值可以根据实际效果微调,比如想让动画结束得早一点就调低百分比,结束得晚一点就调高。
progress() 函数
响应式透明度变化
以前想在屏幕变宽的时候让图片慢慢变淡,得用 calc() 配合百分比计算,但 calc() 在处理不同单位混搭的时候经常翻车。progress() 函数简直就是为这类场景量身定制的,它能轻松算出某个值在指定区间内的进度。
假设想让图片的透明度随着视口宽度变化,视口宽度在 400px 时完全不透明(1),到 1000px 时透明度降到最低(0.25)。代码可以这么写:
.responsive-image {
opacity: clamp(0.25, progress(100vw, 400px, 1000px), 1);
}progress(100vw, 400px, 1000px) 返回的是当前视口宽度在 400px 到 1000px 这个区间内的进度。如果视口宽度小于 400px,函数返回 0;大于 1000px,返回 1。返回的值可能是个小数,比如 0.5。再配合 clamp() 函数,把值限制在 0.25 到 1 之间,就能实现从完全不透明到 0.25 透明度的平滑过渡。
这里有个小细节需要注意,虽然规范上说 progress() 的值会自动 clamp 在 0 和 1 之间,但实际测试发现有时候不 clamp,所以手动加个 clamp() 更保险,避免意外情况发生。
多属性联动
progress() 的魅力在于它返回的是一个数值,这个数值可以被其他属性使用,实现多属性联动。比如不仅透明度变化,还能同时改变模糊程度或者尺寸。
.fancy-element {
--view-progress: progress(100vw, 400px, 1000px);
opacity: clamp(0.25, var(--view-progress), 1);
filter: blur(calc(var(--view-progress) * 10px));
transform: scale(calc(0.8 + var(--view-progress) * 0.2));
}把 progress() 的返回值存成一个 CSS 变量,然后在多个地方复用这个变量,就能让元素的透明度、模糊程度、缩放大小全部根据屏幕宽度同步变化。这种细腻的响应式交互,以前得写一大坨 JavaScript,现在几行 CSS 就搞定了。
绝对定位自对齐
居中大法
想要把一个绝对定位的元素完美居中,老办法得设置 top: 50%; left: 50%; 再配合 transform: translate(-50%, -50%);,这招虽然管用,但总觉得不够优雅。Safari 26 里,justify-self 和 align-self 在绝对定位元素上也能用了,居中这事儿瞬间清爽了不少。
.center-modal {
position: absolute;
justify-self: center;
align-self: anchor-center;
}如果只想在水平方向居中,justify-self: center 就搞定。垂直方向想居中,按理说 align-self: center 应该行,但它会把元素相对于自身居中,而不是视口。这时候得用 align-self: anchor-center,它会以默认的锚点(也就是视口)为参照来垂直居中。两个属性组合起来,就能实现水平和垂直双方向居中。
嫌麻烦的话,可以直接用 place-self 这个简写属性。
.center-modal {
position: absolute;
place-self: anchor-center center;
}place-self: anchor-center center 的第一个值是垂直方向的对齐方式(锚点居中),第二个值是水平方向的对齐方式(居中),一行代码就能把弹窗钉在屏幕正中央,比之前的偏移大法简洁了不止一个档次。
文本对比度与排版
自动高对比文字
给元素设置背景色的时候,选文字颜色总得纠结一下,选白色可能看不清,选黑色又可能刺眼。contrast-color() 函数就像一个智能小助手,它会自动判断在给定背景色上,是白色还是黑色的对比度更高,然后返回那个颜色。
.auto-contrast-card {
--card-bg: coral;
background-color: var(--card-bg);
color: contrast-color(var(--card-bg));
}这样一来,背景色变了,文字颜色也会自动跟着变成更容易看清的那个。不过得留个心眼,contrast-color() 目前只会返回纯黑或纯白,如果背景色本身很复杂,或者需要某种特定的主题色,这个函数可能就帮不上忙了。而且,它只考虑了颜色本身的对比度,没考虑字体粗细、大小这些因素,所以保险起见,最好还是用专业工具验证一下最终的对比度是否达标。
优雅文本折行
长篇大论的段落,英文排版里经常出现最后一行只有一个单词的“寡妇”现象,或者每一行长度参差不齐,看着特别不舒服。text-wrap: pretty 就是专门来解决这个痛点的。
p {
text-wrap: pretty;
}加了这一行样式之后,浏览器会自动优化文本的折行方式,避免出现孤零零的单词独占一行,同时尽量让每一行的长度保持相对均匀,还会控制连字符的使用频率。这样整个段落的视觉感会变得非常整齐,读起来也更顺滑。
