CSS新规范之样式查询
最近,Chrome 团队发布了对一个新的 CSS 规范的实验性支持,即样式查询。简而言之,它让我们查询容器的样式,而不是只查询尺寸。在查询容器尺寸不够的情况下,这可能很有帮助。
CSS 容器查询
介绍样式查询之前,我们先来回顾容器查询。
CSS 容器查询(Container Queries)是一项新的 CSS 功能,允许开发人员根据元素的大小来应用样式。这意味着,开发人员可以为不同大小的设备或浏览器窗口应用不同的样式,而无需使用媒体查询或使用 JavaScript 来检测设备大小。
我们来看一个例子:
.o-grid__item { container-type: inline-size; } .c-article { /* The default style */ } @container (min-width: 400px) { .c-article { /* The styles that will make the article horizontal** ** instead of a card style.. */ } }
首先,我们需要在定义 container-type
。然后,使用 @container
开始查询。一旦满足了这个条件,CSS 将应用于该容器内的组件。
CSS 样式查询
简单地说,样式查询让我们查询一个容器的 CSS 属性或 CSS 变量。
样式查询仍然是试验性的,目前只在 Chrome Canary 中实现。要测试它们,请进入 chrome://flags 并激活 “Experimental Web Platform features”的切换。
例如,我们可以检查容器是否有 display: flex
,并在此基础上为子元素设计样式。
.page-header { display: flex; } @container style(display: flex) { .page-header__start { flex: 1; display: flex; align-items: center; border-right: 1px solid lightgrey; } }
理想情况下,上述做法应该是可行的,但目前 Chrome Canary 中的样式查询原型仅局限于 CSS 变量。样式查询预计将在 Chrome M111 中出现。
现在,我们可以检查变量--boxed: true
是否被添加到容器中,如果是,我们可以在此基础上改变子元素的样式。
请看下图:
请注意,容器查询和样式查询的主要区别在于,前者是针对大小的查询,后者是针对样式的查询。
.card-container { --boxed: true; } @container style(--boxed: true) { .card { /* boxed styles */ } }
问题
在探讨我们可以在哪里使用样式查询之前,我们先来回答大家常见的一个问题:样式查询能解决什么问题?容器查询还不够吗?
这是一个好问题。在容器查询中,我们可以根据一个组件的父级宽度来控制它的样式,这非常有用。不过,在某些情况下,我们可能不需要查询尺寸,而是想查询一个容器的计算样式。
为了让你有更好的了解,请看下图:
这是一篇来自 CMS 的文章正文。我们有一个默认的图片样式和另一个看起来有特色的样式。
下面是对应的代码:
<figure> <img src="cheesecake.jpg" alt="" /> <figcaption>....</figcaption> </figure>
figcaption { font-size: 13px; padding: 4px 8px; background: lightgrey; }
当我们开始对特色的进行造型时,我们需要覆盖上述内容,并有一个 CSS 类,我们可以用它进行造型。
.featured-figure { display: flex; flex-wrap: wrap; } .featured-figure figcaption { font-size: 16px; padding: 16px; border-left: 3px solid; margin-left: -6rem; align-self: center; }
当我们开始为突出显示的元素添加样式时,我们需要覆盖上述样式并定义一个 CSS 类,以便可以对其进行样式设置。
.featured-figure { display: flex; flex-wrap: wrap; } .featured-figure figcaption { font-size: 16px; padding: 16px; border-left: 3px solid; margin-left: -6rem; align-self: center; }
很酷,这个方法行。我们能不能做得更好?是的!使用样式查询,我们可以在 figure 中添加 display: flex
或一个 CSS 变量 --featured: true
,然后基于这个进行样式设置。
<figure> <img src="cheesecake.jpg" alt="" /> <figcaption>....</figcaption> </figure>
figure { container-name: figure; --featured: true; } /* Featured figure style. */ @container figure style(--featured: true) { img { /* Custom styling */ } figcaption { /* Custom styling */ } }
如果 --featured: true
不存在,我们将默认使用基本 figure 设计。我们可以使用 not
关键字来检查 figure 是否没有 display: flex
。
/* Default figure style. */ @container figure not style(--featured: true) { figcaption { /* Custom styling */ } }
要知道的几个细节默认情况下,每个元素都是样式容器
所以根本不需要定义一个样式容器。默认情况下,它就在那里。
我们不能用类名来解决这个问题吗?
是的,我们可以。使用样式查询的目的是使 CSS 更易读并更容易修改。上述逻辑可以作为一个组件 CSS 写出,而无需将所有这些样式添加到条件类中。
例子:点击这里
减少 CSS 特定性问题
我喜欢使用样式查询的原因是,它将减少 CSS 特定性,因为我们将不太依赖 CSS 变化类或 HTML 数据属性来对组件变化进行样式设置。
在下面的 CSS 中,我们为 section 添加了基本样式。没有什么特别的。
.section { background-color: lightgrey; } .section__title, .section__desc { color: #222; }
我们需要一种方法来为它设置不同的主题,因此我们使用了变化类。
.section--dark { background-color: #222; } .section--dark .section__title, .section--dark .section__desc { color: #fff; }
使用样式查询,我们可以在 .section
组件周围使用容器,然后在不在 CSS 中创建更多特定性的情况下为标题和描述打标签。
@container style(--theme: dark) { .section { background-color: #222; } .section__title, .section__desc { color: #fff; } }
这看起来干净多了。
接下来,我们探索几种样式查询可能有帮助的使用情况。
使用情况和示例
基于上下文的样式设置
这是一种常见的使用情况,在同一包装器中我们使用了相同的组件但用法不同。在右侧,我们有一个文章组件,可能包含一个数字或不包含。
目前,我们可能会使用一个新的 CSS 类来解决样式设置问题,或者可能在文章组件本身上使用变化类。
.most-popular { counter-reset: list; } .most-popular article { /* custom styling */ }
或者我们可能在 HTML 中使用 data 属性。
.most-popular[data-counter="true"] { counter-reset: list; } .most-popular[data-counter="true"] .article { /* custom styling */ }
使用 CSS 样式查询,我们可以在父元素中添加一个 CSS 变量,并根据此对文章进行样式设置。看看这个:
.most-popular { --counter: true; } @container style(--counter: true) { .articles-list { counter-reset: list; } .article { display: flex; align-items: flex-start; } .article:before { counter-increment: list; content: counter(list); } }
我们甚至不需要在文章组件上使用变化类。也不需要使用 CSS 嵌套。
例子:点击这里
组件级的主题切换
我们构建的一些组件根据特定条件需要使用不同的主题。在下面的示例中,我们有一个包含不同统计组件的仪表板。
基于包装器,我们需要切换组件的主题。
目前,我们可以使用特殊类根据它们的容器为自定义统计组件添加样式。
.special-wrapper .stat { background-color: #122c46; } .special-wrapper .stat__icon { background-color: #2e547a; } .special-wrapper .stat__title { background-color: #b3cde7; }
上面的做法一点也没有错,也不坏,但因为我们嵌套了 CSS,所以增加了特殊性。让我们探讨一下如何用样式查询来实现上述内容。
首先,我们需要在特殊包装器上定义一个切换按钮。然后,我们可以检查该开关是否处于激活状态,并对状态组件进行相应的设计。
.special-wrapper { --theme: dark; container-name: stats; } @container stats style(--theme: dark) { .stat { /* Add the dark styles. */ } }
在这种情况下,样式查询的有用之处在于,将上述样式放在 CSS 中的一个地方是有意义的。
/* stat.css */ .stat { /* default styling */ } @container stats style(--theme: dark) { .stat { /* custom styling */ } }
头像组
在这个例子中,我们有一组用户的头像。我们需要根据在父代上设置的一个 CSS 变量,以不同的方式来布置它们。我从 Atlassian 设计系统中挑选了这个例子。
html 代码:
<div class="avatars-wrapper"> <div class="avatars-list"> <div class="avatar"></div> <!-- more avatars --> </div> </div>
在 CSS 中,我给容器添加了一个名字,并定义了--appearance: default
变量。
.avatars-wrapper { container-name: avatars; } .avatars-list { display: flex; flex-wrap: wrap; gap: 0.25rem; }
有了这个,我们就可以使用样式查询来改变基于--appearance
变量的布局。
@container avatars style(--appearance: stack) { .avatar { box-shadow: 0 0 0 2px #fff; } .avatar + .avatar { margin-inline-start: -0.5rem; } } @container avatars style(--appearance: grid) { .avatars-list { gap: 0.5rem; max-width: 200px; } }
例子地址:点击这里
条件装饰样式
在某些情况下,我们可能需要根据文本元素在 HTML 中的位置为其添加条件装饰样式。
标题和段落下方有一个旋转的背景效果。这是通过伪元素实现的:
<div class="content"> <h2><!-- Title here --></h2> <p><!-- Description --></p> </div>
要对它们进行样式设置,我们可以使用 CSS 变量并检查它是否已切换,然后相应地添加样式。在示例中,:after
伪元素被添加到 .content
容器的每个子元素。
.content { --decorated: true; } @container style(--decorated: true) { :after { content: ""; position: absolute; inset: 0; background-color: var(--dec-color, green); opacity: 0.1; z-index: -1; transform: rotate(-1.5deg); } }
例子地址:点击这里
RTL 样式:卡片组件
写 RTL 样式时,第一步是在 <html>
元素中添加 dir=rtl
。一旦添加,每个元素的 direction CSS 属性都会变为 direction: rtl
。
随着逻辑属性的兴起,我们不需要完全重写 CSS。考虑以下示例:
.item { margin-inline-start: 1rem; }
对于从左到右的布局,上述内容将计算为 margin-left
。对于从右到左的布局,它将是 margin-right
。很酷,对吧?但是我们仍然没有检查渐变方向的逻辑 CSS。
样式查询可以用于解决这个问题。考虑以下示例:
我们有一个组件,由两个元素组成,这两个元素都应根据文档改变方向:
- 渐变:对于 LTR 布局,它从左到右。
- 箭头方向:指向右边。
- 上述内容无法使用逻辑 CSS 控制。目前,我们这样做:
html[dir="rtl"] .card { background: linear-gradient(to left, ...); } html[dir="rtl"] .card__cta { transform: scaleX(-1); }
使用样式查询,我们可以查询容器并检查direction
是否等于 rtl
,并根据此对样式进行更改。
.card { --bg-angle: to right; background: linear-gradient(var(--bg-angle), #5521c3, #5893eb); } @container card style(direction: rtl) { .card { --bg-angle: to left; } .card__cta { transform: scaleX(-1); } }
请注意,样式查询的当前原型不支持 style() 查询中的 CSS 属性。因此,我在示例中使用了 CSS 变量。
新闻模块
我在某网站上发现的真实问题。最初,我们有以下新闻组件。
根据其容器,样式应略有改变。考虑以下图:
注意组件现在有两个修改:
- 白色背景。
- 标题和描述容器四周填充。
- 这是 CSS 的样式:
.media--padded { background: #fff; } .media--padded .media__content { padding: 0.75rem 0.75rem 3rem 0.75rem; }
我们如何通过样式查询来解决这个问题呢?很简单,我们需要一种方法来告诉组件,如果你包含在这个容器内,卡片的样式应该被填充。
.special-container { --card--padded: true; } @container style (--card-padded: true) { .media { background: #fff; } .media__content { padding: 0.75rem 0.75rem 3rem 0.75rem; } }
结语
CSS 样式查询是 CSS 的强大补充。如果让大家使用,你会用它来做什么呢?
码云笔记 » CSS新规范之样式查询