Chrome and Safari browsers have fully supported the :has()
pseudo-class of CSS, which is gradually being launched in many browsers. It is often called a "parent selector" - we can select and set the style of the parent element through child selectors - but :has()
is used far more than that. One of the uses is to redesign the clickable card mode that many people often use.
We will explore :has()
How to help us with link cards, but first...
:has()
What is a pseudo-class? There are many excellent articles that explain the purpose of :has()
well, but it is still relatively new, so we should also briefly introduce it here.
:has()
is a relational pseudo-class that is part of the W3C selector level 4 working draft. This is what brackets are for: match elements associated with a specific child element (more precisely, containing a specific child element).
<code>/* 匹配包含图像元素的article元素 */ article:has(img) { } /* 匹配article元素,其内部直接包含图像 */ article:has(> img) { }</code>
So you can understand why we might refer to it as a "parent" selector. But we can also use it in conjunction with other functional pseudo-classes to get a more specific match. Suppose we want to style the article does not contain any images. We can combine the relationship ability of with the negative ability of :has()
to achieve this: :not()
<code>/* 匹配不包含图像的article元素 */ article:not(:has(img)) { }</code>
. Before we specifically solve the clickable card puzzle, let's take a look at some of the ways to deal with them without using :has()
. :has()
"Link as wrapper" method
There are many issues here, especially when it comes to accessibility. When users browse your website using the rotator feature, they hear the full text inside the
element - title, text, and link. Some people may not want to listen to all of this. We can do better. Starting with HTML5, we can nest block elements in <a></a>
elements. But this always feels wrong to me, especially for this reason. <a></a>
Pros:
Disadvantages:
This method has many benefits. Our links are accessible when focused and we can even select text. But there are some disadvantages in terms of style. For example, if we want to animate these cards, we have to add the .card
style in the :hover
main wrapper instead of the link itself. We also don't benefit from animation when the link is focused through the keyboard tab keys.
Pros:
Disadvantages:
::after
Selector method This method requires us to set the card to relative positioning and then set the absolute positioning on the linked ::after
pseudo selector. This does not require any JavaScript and is easy to implement:
There are some drawbacks here, especially when it comes to selecting text. You won't be able to select text unless you provide a higher z-index on the card body, but if you do, be aware that clicking on text won't activate your link. Whether you want selectable text is up to you. I think this might be a UX issue, but it depends on the use case. The text is still accessible via a screen reader, but my main problem with this approach is the lack of animation possibilities.
Pros:
Disadvantages:
::after
in combination with :has()
Now that we have identified existing methods for clickable cards, I would like to show how to add :has()
to the mix to solve most of these shortcomings.
In fact, let's base this approach on the method we last viewed using ::after
on the link element. We can actually use :has()
there to overcome the animation limitations of the method.
Let's start with the marker:
<code>/* 匹配包含图像元素的article元素 */ article:has(img) { } /* 匹配article元素,其内部直接包含图像 */ article:has(> img) { }</code>
To keep it simple, I will position elements directly in CSS, instead of using classes.
For this demo, we will add image scaling and shadows to the card for hovering and animate the links so that the arrow pops up and changes the text color of the links. To simplify operations, we will add some scoped custom properties to the card. This is the basic style:
<code>/* 匹配包含图像元素的article元素 */ article:has(img) { } /* 匹配article元素,其内部直接包含图像 */ article:has(> img) { }</code>
Very good! We have added the initial scaling (--img-scale: 1.001
), the initial color of the card title (--title-color: black
) and some extra properties we will use to make the arrow pop up from the link. We also set the empty state of the box-shadow
declaration to be animate it later. This sets the basis for the clickable cards we need to create now, so let's add some resets and styles by adding these custom properties to the elements we want to animate:
<code>/* 匹配不包含图像的article元素 */ article:not(:has(img)) { }</code>
Let's be user-friendly and add a class hidden in the screen reader behind the link:
<img src="/static/imghw/default1.png" data-src="https://img.php.cn/" class="lazy" alt="Creating Animated, Clickable Cards With the :has() Relational Pseudo Class "><div clas="article-body"> <h2>Some Heading</h2> <p>Curabitur convallis ac quam vitae laoreet. Nulla mauris ante, euismod sed lacus sit amet, congue bibendum eros. Etiam mattis lobortis porta. Vestibulum ultrices iaculis enim imperdiet egestas.</p> <a href="https://www.php.cn/link/93ac0c50dd620dc7b88e5fe05c70e15b"> Read more <svg fill="currentColor" viewbox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" fill-rule="evenodd"></path></svg></a> </div>
Our cards are starting to look beautiful. It's time to add some magic to it. Using the :has()
pseudo-class, we can now check if our link is hovered or focused, then update our custom properties and add box-shadow
. With this small amount of CSS code, our cards are truly come to life:
/* 卡片元素 */ article { --img-scale: 1.001; --title-color: black; --link-icon-translate: -20px; --link-icon-opacity: 0; position: relative; border-radius: 16px; box-shadow: none; background: https://www.php.cn/link/93ac0c50dd620dc7b88e5fe05c70e15bfff; transform-origin: center; transition: all 0.4s ease-in-out; overflow: hidden; } /* 链接的::after伪元素 */ article a::after { content: ""; position: absolute; inset-block: 0; inset-inline: 0; cursor: pointer; }
Did you see the above content? Now, if any child elements in the card are hovered or focused, we will get updated styles. Even if the link element is the only element that can contain a hover or focus state in the clickable card method, we can use it to match the parent element and apply the transformation.
::after
That's it. Another powerful use case for selectors. We can not only match the parent element by declaring other elements as parameters, but also use pseudo-classes to match and set the style of the parent element.
:has()
Pros:
Accessible
:hover
Text is not easy to choose.
The above is the detailed content of Creating Animated, Clickable Cards With the :has() Relational Pseudo Class. For more information, please follow other related articles on the PHP Chinese website!