在 scroll-state()
查询中,你可以使用以下三个 @container
描述符:
scrollable
:查询容器是否可以通过用户启动的滚动(例如通过拖动滚动条或使用触控板手势)在给定方向上滚动。换句话说,给定方向上是否有任何溢出内容可以滚动到?这对于应用与滚动容器的滚动位置相关的样式非常有用。例如,你可以在滚动条位于顶部时显示一个提示,鼓励人们向下滚动查看更多内容,并在用户实际开始滚动时将其隐藏。
snapped
:查询容器是否将沿给定轴吸附到滚动吸附容器祖先。这对于在元素吸附到滚动吸附容器时应用样式非常有用。例如,你可能希望以某种方式突出显示吸附的元素,或者显示其以前隐藏的某些内容。
stuck
:查询具有 sticky
position
值的容器是否粘滞到其滚动容器祖先的边缘。这对于在 position: sticky
元素粘滞时对其进行不同的样式设置非常有用——例如,你可以为它们提供不同的配色方案或布局。
要将容器元素建立为滚动状态查询容器,请在其上设置 container-type
属性,并将其值设置为 scroll-state
。你也可以选择为其指定一个 container-name
,以便可以使用特定的容器查询来定位它。
.container {
container-type: scroll-state;
container-name: my-container;
}
然后,你可以创建一个 @container
块,该块指定查询、如果测试通过则应用于容器子元素的规则,以及可选的要查询的容器的 container-name
。如果你不指定 container-name
,则容器查询将应用于页面上的所有滚动状态查询容器。
在这里,我们只查询名为 my-container
的容器,以确定容器是否可以向其顶部边缘滚动。
@container my-container scroll-state(scrollable: top) {
/* CSS rules go here */
}
注意:为了将滚动状态查询与其他容器查询分开,滚动状态描述符和值放置在括号内,前面加上 scroll-state
(scroll-state( ... )
)。这些构造看起来像函数,但它们不是。
滚动状态 scrollable
查询,写为 scroll-state(scrollable: value)
,测试容器的滚动祖先是否可以通过用户启动的滚动在给定方向上滚动。如果不能,查询返回 false。
value
表示你正在测试滚动可用性的方向,例如:
top
:测试容器是否可以向其顶部边缘滚动。
inline-end
:测试容器是否可以向其内联结束边缘滚动。
y
:测试容器是否可以沿其 y 轴的任一或两个方向滚动。
如果测试通过,则 @container
块内的规则将应用于匹配滚动容器的后代。
让我们看一个例子,其中我们有一个充满内容的滚动容器,以及一个方便的小链接,如果需要可以滚动回顶部。我们将使用 scrollable
查询,仅当用户开始向下滚动内容时才显示该链接。
在 HTML 中,我们有一个 <article>
元素,其中包含足够的内容以使文档滚动,前面是一个返回顶部链接。
<a class="back-to-top" href="#" aria-label="Top of page">↑</a>
<article>
<h1>Reader with container query-controlled "back-to-top" link</h1>
<section>
<header>
<h2>This first section is interesting</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</header>
...
</section>
...
</article>
为了简洁起见,我们隐藏了大部分 HTML。
<a class="back-to-top" href="#" aria-label="Scroll back to top">↑</a>
<article>
<h1>Reader with container query-controlled "back-to-top" link</h1>
<section>
<header>
<h2>This first section is interesting</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</header>
<p>
Mauris non malesuada est, sed vestibulum nibh. Duis vestibulum iaculis
lectus, eu sagittis dolor dignissim iaculis. Nunc et orci sed sapien
eleifend placerat. Curabitur dapibus risus eget odio sollicitudin, sit
amet luctus justo pellentesque.
</p>
<p>
<strong>Morbi non pharetra quam.</strong> Fusce vestibulum sem diam, ac
consequat augue consectetur ut. Donec at augue viverra, tempus urna sit
amet, porta augue.
<em>Phasellus fringilla tincidunt sem ullamcorper varius.</em> Aenean
gravida feugiat sem nec ultricies.
</p>
<img src="#" alt="Placeholder" />
<p>
Sed pellentesque placerat mi sed maximus. Sed vitae dui vitae mi pulvinar
gravida sed et libero.
<a href="#">Duis nec venenatis dolor, sed tristique felis.</a>
Integer dapibus facilisis leo elementum vulputate. Curabitur a urna quis
nulla vulputate tincidunt quis ac enim.
</p>
<p>
Cras non elit vel leo dignissim convallis. Duis eros urna, varius sit amet
lorem vel, feugiat euismod est.
<strong>Aliquam ornare eu elit ut iaculis.</strong>
Suspendisse vulputate tempor leo, non rhoncus risus aliquam vel.
</p>
</section>
<section>
<header>
<h2>This one, not so much</h2>
<p>Suspendisse varius est ac turpis mollis cursus.</p>
</header>
<p>
<strong
>Curabitur faucibus condimentum eros, ut auctor felis lacinia
sed.</strong
>
Praesent vitae scelerisque eros.
</p>
<p>
<em>Ut vitae suscipit augue.</em> Cras et orci condimentum ante dignissim
iaculis. Sed consectetur quis est sed dignissim. Nulla egestas orci erat,
et commodo arcu feugiat ut.
</p>
<img src="#" alt="Placeholder" />
<p>
Sed non tempor massa, at accumsan ante. Pellentesque habitant morbi
<a href="#">tristique senectus</a> et netus et malesuada fames ac turpis
egestas.
</p>
<p>
Pellentesque placerat luctus tempor. Nunc congue dapibus eros, at
vulputate nulla. Sed rutrum eleifend magna vel porta. Integer cursus orci
faucibus turpis scelerisque, nec pharetra arcu molestie.
</p>
</section>
<section>
<header>
<h2>Hopefully this section provides some clarity?</h2>
<p>Curabitur facilisis ornare lorem et eleifend.</p>
</header>
<p>
<strong>Aenean mollis non neque sed finibus.</strong> Lorem ipsum dolor
sit amet, consectetur adipiscing elit. Suspendisse sagittis viverra urna.
In hac habitasse platea dictumst. Vestibulum neque orci, mollis sagittis
augue et, pharetra vehicula diam.
</p>
<img src="#" alt="Placeholder" />
<p>
<a href="#">Pellentesque sollicitudin</a> nunc quis nisl condimentum, ac
iaculis libero feugiat.
<strong>Nullam ultrices purus a nulla dignissim hendrerit.</strong> In
molestie consectetur est quis pulvinar.
</p>
<p>
Vivamus ac erat eu est lobortis commodo. Orci varius natoque penatibus et
magnis dis parturient montes, nascetur ridiculus mus. In nulla turpis,
<strong>mollis et est tempor</strong>, dignissim aliquam metus. Proin eu
arcu quis erat mollis pulvinar. Vivamus at facilisis neque.
</p>
<p>
Integer bibendum laoreet erat, quis vulputate mauris bibendum nec. Class
aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
himenaeos. Nam ut est in arcu interdum hendrerit.
</p>
</section>
<section>
<header>
<h2>A summary of sorts</h2>
<p>Nunc facilisis augue quis ex porta aliquam.</p>
</header>
<img src="#" alt="Placeholder" />
<p>
<strong
>Fusce nisi enim, venenatis a est vel, varius placerat lacus.</strong
>
Nunc tempus rutrum nisl bibendum aliquet. Pellentesque vitae nunc sed nisl
tincidunt elementum a sit amet nisi. Morbi pretium at dolor in pulvinar.
Curabitur dapibus eleifend accumsan.
</p>
<p>
Donec rhoncus, leo vitae mollis maximus, tellus lorem interdum arcu, eu
<em>tempor lectus libero in risus</em>. Ut sit amet magna vitae mauris
tempor bibendum. <a href="#">Integer id mauris ut ex mattis finibus.</a>
</p>
<p>
Curabitur dui felis, elementum et tellus id, blandit facilisis lorem.
Aliquam sed posuere ligula, at auctor ipsum. Morbi dignissim accumsan
tellus pretium iaculis.
</p>
</section>
</article>
.back-to-top
链接被赋予 fixed
的 position
值,放置在视口的右下角,并使用 80px 0
的 translate
值移出视口。当任一值更改时,transition
值将动画化 translate
和 background-color
。
/* General styling */
* {
box-sizing: border-box;
}
html {
font-family: "Helvetica", "Arial", sans-serif;
height: 100%;
}
body {
height: inherit;
width: 100%;
max-width: 540px;
padding: 20px;
margin: 0 auto;
}
p {
line-height: 1.5;
}
img {
display: block;
width: 90%;
margin: 30px auto;
padding: 20px;
border: 2px solid gray;
aspect-ratio: 3/2;
}
.back-to-top {
width: 64px;
height: 64px;
color: white;
text-align: center;
position: fixed;
bottom: 10px;
right: 10px;
translate: 80px 0;
transition:
0.4s translate,
0.2s background-color;
}
.back-to-top {
text-decoration: none;
border-radius: 50%;
border: 1px solid #00000077;
background-color: #00000077;
color: white;
font-size: 3rem;
text-shadow: 0 0 2px black;
padding-bottom: 10px;
}
.back-to-top:hover,
.back-to-top:focus {
background: #00000099;
}
此示例中的滚动容器是 <html>
元素本身,它被指定为滚动状态查询容器,其 container-type
值为 scroll-state
。container-name
并非严格必需,但在代码添加到具有多个通过不同查询定位的滚动状态查询容器的代码库中时非常有用。
html {
container-type: scroll-state;
container-name: scroller;
}
接下来,我们定义一个 @container
块,该块设置此查询的目标容器名称,以及查询本身——scrollable: top
。此查询仅当 <html>
元素可以向其顶部边缘滚动时才应用块内包含的规则——换句话说,如果容器之前已向下滚动。如果是这种情况,则将 translate: 0 0
应用于 .back-to-top
链接,这将使其平滑地回到屏幕上。
@container scroller scroll-state(scrollable: top) {
.back-to-top {
translate: 0 0;
}
}
为了简洁起见,我们隐藏了示例 CSS 的其余部分。
尝试向下滚动文档,并注意“返回顶部”链接如何因此出现,由于 transition
而从视口右侧平滑动画。如果你通过激活链接或手动滚动返回顶部,“返回顶部”链接将平滑地移出屏幕。
仅当实现滚动吸附时才相关,滚动状态snapped
查询(写为scroll-state(snapped: value)
)测试容器是否将沿给定轴吸附到滚动吸附容器祖先。如果不是,查询返回 false。
在这种情况下,value
表示你正在测试元素吸附能力的方向,例如:
x
:测试容器是否水平吸附到其滚动吸附容器祖先。
inline
:测试容器是否在内联方向上吸附到其滚动吸附容器祖先。
y
:测试容器是否在两个方向上吸附到其滚动吸附容器祖先。
要评估具有非 none
snapped
滚动状态查询的容器,它必须是一个具有滚动吸附容器祖先的容器,也就是说,该祖先的 scroll-snap-type
值不是 none
。容器查询 scroll-state(snapped: none)
匹配没有滚动容器祖先的滚动状态容器。
评估将在滚动吸附容器上触发 scrollsnapchanging
事件时进行。
如果测试通过,则 @container
块内的规则将应用于匹配的滚动吸附目标容器的后代。
在此示例中,我们将查看一个具有垂直吸附子元素的滚动吸附容器,并使用 snapped
查询仅在子元素吸附或即将吸附时为其设置样式。
HTML 由一个 <main>
元素组成,该元素将成为滚动吸附容器。内部有几个 <section>
元素,它们将成为吸附目标。每个 <section>
都包含一个包装器 <div>
和一个 <h2>
标题。包含包装器的目的是创建一个样式目标,因为容器查询允许对容器的后代进行样式设置,而不是容器本身。
<main>
<section>
<div class="wrapper">
<h2>Section 1</h2>
</div>
</section>
...
</main>
为了简洁起见,我们隐藏了大部分 HTML。
<main>
<section>
<div class="wrapper">
<h2>Section 1</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 2</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 3</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 4</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 5</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 6</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 7</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 8</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 9</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 10</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 11</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 12</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 13</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 14</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 15</h2>
</div>
</section>
<section>
<div class="wrapper">
<h2>Section 16</h2>
</div>
</section>
</main>
* {
box-sizing: border-box;
}
html {
height: 100%;
}
/* body and main sizing */
body {
display: flex;
align-items: center;
justify-content: center;
height: inherit;
}
main {
gap: 50px;
}
我们在 <main>
元素上设置了 scroll
的 overflow
值和固定的 height
,以将其转换为垂直滚动容器。我们还设置了 y mandatory
的 scroll-snap-type
值,将 <main>
转换为滚动吸附容器,吸附目标将沿 y 轴吸附到该容器;mandatory
意味着吸附目标将始终被吸附到。
main {
overflow: scroll;
scroll-snap-type: y mandatory;
height: 450px;
width: 250px;
border: 3px solid black;
}
通过设置非 none
的 scroll-snap-align
值,将 <section>
元素指定为吸附目标。center
值表示它们将以其中心点吸附到容器。
section {
font-family: "Helvetica", "Arial", sans-serif;
width: 150px;
height: 150px;
margin: 50px auto;
scroll-snap-align: center;
}
.wrapper {
width: 100%;
height: 100%;
border-radius: 5px;
background: #eeeeee;
box-shadow:
inset 1px 1px 4px rgb(255 255 255 / 0.5),
inset -1px -1px 4px rgb(0 0 0 / 0.5);
display: flex;
align-items: center;
justify-content: center;
transition:
0.6s background,
0.6s color;
}
h2 {
font-size: 1rem;
letter-spacing: 1px;
}
我们希望能够查询 <section>
元素。具体来说,我们希望测试 <section>
元素是否正在吸附到其容器,因此我们通过在其上设置 scroll-state
的 container-type
值,将它们指定为滚动状态查询容器。我们还为它们指定了一个 container-name
,这并非严格必需,但如果我们的代码以后变得更复杂,并且我们有多个希望使用不同查询定位的滚动状态查询容器,则会很有用。
section {
container-type: scroll-state;
container-name: snap-container;
}
接下来,我们定义一个 @container
块,该块设置我们在此查询中定位的容器名称以及查询本身——snapped: y
。此查询仅当 <section>
元素垂直吸附到其容器时才应用块中包含的规则。如果是这种情况,我们将新的 background
和 color
应用于 <section>
元素的子 .wrapper
<div>
以突出显示它。
@container snap-container scroll-state(snapped: y) {
.wrapper {
background: purple;
color: white;
}
}
渲染结果如下所示。尝试上下滚动容器,并注意当 <section>
吸附到其容器时,其样式如何变化。
滚动状态stuck
查询(写为scroll-state(stuck: value)
)测试具有sticky
position
值的容器是否粘滞到其滚动容器祖先的边缘。如果不是,则查询返回 false。
在这种情况下,value
表示你正在测试的滚动容器边缘,例如:
top
:测试容器是否粘滞到其滚动容器祖先的顶部边缘。
block-end
:测试容器是否粘滞到其滚动容器祖先的块结束边缘。
none
:测试容器是否未粘滞到其滚动容器祖先的任何边缘。请注意,即使容器未设置 position: sticky
,none
查询也会匹配。
如果查询返回 true,则 @container
块内的规则将应用于匹配的 position: sticky
容器的后代。
让我们看一个例子,其中有一个包含溢出内容的滚动容器,其中的标题设置为 position: sticky
,并在滚动到该位置时粘滞到容器的顶部边缘。我们将使用 stuck
滚动状态查询,以便在标题粘滞到顶部边缘时对其进行不同的样式设置。
在 HTML 中,我们有一个 <article>
元素,其中包含足够的内容以导致文档滚动。它使用几个 <section>
元素进行结构化,每个元素都包含一个带有嵌套内容的 <header>
。
<article>
<h1>Sticky reader with scroll-state container query</h1>
<section>
<header>
<h2>This first section is interesting</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</header>
...
</section>
<section>
<header>
<h2>This one, not so much</h2>
<p>Confecta res esset.</p>
</header>
...
</section>
...
</article>
为了简洁起见,我们隐藏了大部分 HTML。
<article>
<h1>Sticky reader with scroll-state container query</h1>
<section>
<header>
<h2>This first section is interesting</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</header>
<p>
Id deserunt exercitation in incididunt in dolor ipsum enim occaecat quis
officia et. In dolore Lorem anim enim. Dolor nulla deserunt laboris irure
incididunt labore tempor amet consectetur. Commodo eu tempor qui laboris
duis ipsum adipisicing sunt. Do anim laboris commodo aliquip consequat ut
eiusmod mollit sint nostrud sunt exercitation do.
</p>
<p>
<strong>Est proident voluptate dolore.</strong> Aute irure ea culpa mollit
<em>anim aute proident consectetur.</em> Duis deserunt consequat elit non.
Cupidatat et amet anim labore est. Incididunt aute incididunt ex amet
sint.
</p>
<img src="#" alt="Placeholder" />
<p>
Ullamco duis qui amet sunt eu nulla deserunt dolore officia irure nulla.
<a href="#">Qui enim dolore est virtus;</a>
Erat enim consectetur.
</p>
<p>
Dolor aliquip officia id esse Lorem ullamco ea veniam.
<strong>Voluptate cillum commodo anim veniam;</strong>
Ad nulla amet anim eu incididunt reprehenderit proident in aliqua sit
commodo magna.
</p>
</section>
<section>
<header>
<h2>This one, not so much</h2>
<p>Confecta res esset.</p>
</header>
<p>
<strong>Nam quid possums facere delius?</strong> Tu quidem reddes; Omnis
peccant para dicts.
</p>
<p>
<em>Pariatur ex cupidatat exercitation</em> reprehenderit quis culpa est
proident pariatur ut.
</p>
<img src="#" alt="Placeholder" />
<p>
Non nulla pariatur cillum pariatur.
<a href="#">Ipsum magna proident fugiat qui duis et.</a>
Reprehenderit sit ipsum ut adipisicing aliqua eiusmod nulla consectetur
deserunt dolor dolor.
</p>
<p>
Cupidatat deserunt adipisicing adipisicing occaecat voluptate duis laboris
excepteur labore et in aliquip aute. Quis aliquip reprehenderit dolor
nostrud cupidatat nostrud nisi occaecat elit. Magna dolor ullamco eiusmod
deserunt elit cillum qui amet. Dolor ad consequat anim commodo eu sunt do
tempor eu velit est.
</p>
</section>
<section>
<header>
<h2>Hopefully this section provides some clarity?</h2>
<p>Dolor consectetur esse aute eiusmod ex reprehenderit cupidatat.</p>
</header>
<p>
<strong>Ipsum quis sint incididunt aliquip</strong>. Duis tempor sunt enim
Lorem ut officia cillum magna.
</p>
<img src="#" alt="Placeholder" />
<p>
<a href="#">Sint modo partes vitae beatae.</a>
Ipsum esse nostrud incididunt ipsum ipsum esse aliquip Lorem enim.
<strong>Sed hoc sane concedays.</strong> Voluptate qui consequat dolor
dolor dolore ex. Enim dolor enim sit proident. Ad laborum incididunt
dolore aliquip exercitation exercitation. Sunt cupidatat esse enim duis
proident.
</p>
<p>
Labore dolor cupidatat ut velit in minim cupidatat enim nisi proident
eiusmod.
<strong
>Non nostrud consectetur est occaecat ut esse esse tempor eiusmod
non.</strong
>
Cupidatat aliqua dolore est sit ad exercitation nostrud nostrud eu. Qui
consequat et aliquip ea labore.
</p>
<p>
Pariatur quis quis nostrud exercitation pariatur magna veniam Lorem
commodo cupidatat irure reprehenderit. Nostrud pariatur est pariatur elit
sit ea voluptate minim mollit. Aliqua pariatur cupidatat et cillum. Velit
exercitation adipisicing laboris ullamco. Incididunt id eiusmod id cillum
qui anim. Aliquip minim nostrud excepteur dolore deserunt pariatur eu
eiusmod reprehenderit ullamco magna.
</p>
</section>
<section>
<header>
<h2>A summary of sorts</h2>
<p>Irure adipisicing occaecat dolore in ex voluptate.</p>
</header>
<img src="#" alt="Placeholder" />
<p>
<strong>An quidem modi?</strong>
<strong
>Excepteur non Lorem ex aliqua est reprehenderit incididunt sunt in
ullamco in et;</strong
>
Aliqua veniam nulla magna anim labore amet proident nisi mollit non.
Consequat incididunt exercitation nisi consequat culpa officia aliqua
veniam. Anim aliqua consectetur pariatur mollit proident incididunt id.
</p>
<p>
Non labore, inquit, de nomin. <em>Fall igniter possimus.</em>
<a href="#">Preterits, inquit, gaud.</a>
Est commodo adipisicing qui duis excepteur sit ea irure amet voluptate
deserunt anim deserunt.
</p>
<p>
Ad mollit laboris reprehenderit magna tempor. Consequat pariatur esse
ipsum duis laboris.
</p>
</section>
</article>
每个 <header>
都具有 sticky
的 position
值和 0
的 top
值,这使得它们粘滞到滚动容器的顶部边缘。为了测试 <header>
元素是否粘滞到容器顶部边缘,它们被指定为滚动状态查询容器,其 container-type
值为 scroll-state
。container-name
并非严格必需,但如果此代码添加到具有多个通过不同查询定位的滚动状态查询容器的代码库中,则会很有用。
/* General styling */
* {
box-sizing: border-box;
}
html {
font-family: "Helvetica", "Arial", sans-serif;
height: 100%;
}
body {
height: inherit;
width: 100%;
max-width: 540px;
padding: 20px;
margin: 0 auto;
}
p {
line-height: 1.5;
}
img {
display: block;
width: 90%;
margin: 30px auto;
padding: 20px;
border: 2px solid gray;
aspect-ratio: 3/2;
}
header {
background: white;
position: sticky;
top: 0;
container-type: scroll-state;
container-name: sticky-heading;
}
我们还为 <header>
元素中的 <h2>
和 <p>
元素提供了一些基本样式,以及一个 transition
值,以便当它们的 background
值更改时,它们会平滑地动画。
h2,
header p {
margin: 0;
transition: 0.4s background;
}
h2 {
padding: 20px 5px;
margin-bottom: 10px;
}
header p {
font-style: italic;
padding: 10px 5px;
}
接下来,我们定义一个 @container
块,该块设置我们在此查询中定位的容器名称以及查询本身——stuck: top
。此查询仅当 <header>
元素粘滞到其滚动容器的顶部时才应用块中包含的规则。在这种情况下,不同的 background
和 box-shadow
将应用于包含的 <h2>
和 <p>
。
@container sticky-heading scroll-state(stuck: top) {
h2,
p {
background: #cccccc;
box-shadow: 0 5px 2px #00000077;
}
}
为了简洁起见,我们隐藏了 CSS 的其余部分。
尝试上下滚动文档,并注意当 <h2>
和 <p>
元素粘滞到其容器顶部边缘时,它们如何转换为新的配色方案。