Monday, March 13, 2023
HomeWeb DevelopmentCreating Animated, Clickable Playing cards With the :has() Relational Pseudo Class |...

Creating Animated, Clickable Playing cards With the :has() Relational Pseudo Class | CSS-Tips

The CSS :has() pseudo class is rolling out in lots of browsers with Chrome and Safari already totally supporting it. It’s typically referred to it as “the dad or mum selector” — as in, we are able to choose model a dad or mum ingredient from a toddler selector — however there may be a lot extra that :has() will help us remedy. A type of issues is re-inventing the clickable card sample many people love to make use of now and again.

We’ll check out how :has() will help us deal with linked playing cards, however first…

What is that this :has() pseudo class?

There’s already a bunch of nice posts floating round that do a superb job explaining what :has() is and what it’s used for, but it surely’s nonetheless new sufficient that we should say a number of phrases about it right here as properly.

:has() is a relational pseudo class that’s a part of the W3C Selectors Stage 4 working draft. That’s what the parentheses are all about: matching parts which might be associated to — or, extra precisely, include — sure baby parts.

/* Matches an article ingredient that accommodates a picture ingredient */
article:has(img) { }

/* Matches an article ingredient with a picture contained instantly inside it */
article:has(> img) { }

So, you possibly can see why we would wish to name it a “dad or mum” selector. However we are able to additionally mix it with different practical pseudo courses to get extra particular. Say we wish to model articles that do not include any photos. We will mix the relational powers of :has() with the negation powers of :not() to try this:

/* Matches an article with out photos  */
article:not(:has(img)) { }

However that’s simply the beginning of how we are able to mix powers to do extra with :has(). Earlier than we flip particularly to fixing the clickable card conundrum, let’s have a look at a number of methods we at present strategy them with out utilizing :has().

How we at present deal with clickable playing cards

There are three predominant approaches on how individuals create a totally clickable card nowadays and to totally perceive the ability of this pseudo class, it’s good to have a little bit of a round-up.

This strategy is one thing used fairly regularly. I by no means use this strategy however I created a fast demo to reveal it:

There are plenty of issues right here, particularly in the case of accessibility. When customers navigate your web site utilizing the rotor operate, they’ll hear the total textual content within that <a> ingredient — the heading, the textual content, and the hyperlink. Somebody may not wish to sit by way of all that. We will do higher. Since HTML5, we are able to nest block parts within an <a> ingredient. However it by no means feels proper to me, particularly for that reason.


  • Fast to implement
  • Semantically right


  • Accessibility issues
  • Textual content not selectable
  • Lots of problem to overwrite types that you just used in your default hyperlinks

The JavaScript technique

Utilizing JavaScript, we are able to connect a hyperlink to our card as an alternative of writing it within the markup. I discovered this nice CodePen demo by costdev who additionally made the cardboard textual content selectable within the course of:

This strategy has plenty of advantages. Our hyperlinks are accessible on focus and we are able to even choose textual content. However there are some drawbacks in the case of styling. If we wish to animate these playing cards, for instance, we must add :hover types on our predominant .card wrapper as an alternative of the hyperlink itself. We additionally wouldn’t profit from the animations when the hyperlinks are in focus from keyboard tabbing.


  • Could be made completely accessible
  • Potential to pick textual content


  • Requires JavaScript
  • Proper clicking not attainable (though could possibly be mounted with some additional scripting)
  • Would require plenty of styling on the cardboard itself which might not work when focussing the hyperlink

The ::after selector strategy

This technique requires us to set the cardboard with relative positioning, then set absolute positioning on the hyperlink’s ::after pseudo selector of a hyperlink. This doesn’t require any JavaScript and is fairly simple to implement:

There are a number of drawbacks right here, particularly in the case of deciding on textual content. Until you present the next z-index in your card-body, you gained’t be capable to choose textual content however for those who do, be warned that clicking the textual content won’t activate your hyperlink. Whether or not or not you need selectable textual content is as much as you. I feel it may be a UX difficulty, but it surely will depend on the use-case. The textual content continues to be accessible to display screen readers however my predominant drawback with the strategy is the shortage of animation potentialities.


  • Straightforward to implement
  • Accessible hyperlink with out bloated textual content
  • Works on hover and focus


  • Textual content is just not selectable
  • You’ll be able to solely animate the hyperlink as that is the ingredient you’re hovering.

A brand new strategy: Utilizing ::after with :has()

Now that we’ve established the prevailing approaches for clickable playing cards, I wish to present how introducing :has() to the combination solves most of these shortcomings.

In reality, let’s base this strategy on the final one we checked out utilizing ::after on the hyperlink ingredient. We will truly use :has() there to beat that strategy’s animation constraints.

Let’s begin with the markup:

    <img src="" alt="Fluffy grey and white tabby kitten snuggled up in a ball." />
  <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="#">
      Learn extra
       <svg xmlns="" class="icon" viewBox="0 0 20 20" fill="currentColor">
         <path fill-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" clip-rule="evenodd" />

I can be conserving issues so simple as attainable by concentrating on parts within the CSS as an alternative of courses.

For this demo, we’re going so as to add a picture zoom and shadow to the cardboard on hover, and animate the hyperlink with an arrow popping up and whereas altering the hyperlink’s textual content coloration. To make this simple, we’re going so as to add some customized properties scoped on our card. Right here’s the essential styling:

/* The cardboard ingredient */
article {
  --img-scale: 1.001;
  --title-color: black;
  --link-icon-translate: -20px;
  --link-icon-opacity: 0;

  place: relative;
  border-radius: 16px;
  box-shadow: none;
  background: #fff;
  transform-origin: middle;
  transition: all 0.4s ease-in-out;
  overflow: hidden;
/* The hyperlink's ::after pseudo */
article a::after {
  content material: "";
  place: absolute;
  inset-block: 0;
  inset-inline: 0;
  cursor: pointer;

Nice! We added an preliminary scale for the picture (--img-scale: 1.001), the preliminary coloration of the cardboard heading (--title-color: black) and a few additional properties we’ll use to make our arrow come out of the hyperlink. We’ve additionally set an empty state of the box-shadow declaration with the intention to animate it later . This units up what we’d like for the clickable card proper now, so let’s add some resets and styling to it by including these customized properties to the weather we wish to animate:

article h2 {
  margin: 0 0 18px 0;
  font-family: "Bebas Neue", cursive;
  font-size: 1.9rem;
  letter-spacing: 0.06em;
  coloration: var(--title-color);
  transition: coloration 0.3s ease-out;
article determine {
  margin: 0;
  padding: 0;
  aspect-ratio: 16 / 9;
  overflow: hidden;
article img {
  max-width: 100%;
  transform-origin: middle;
  remodel: scale(var(--img-scale));
  transition: remodel 0.4s ease-in-out;
article a {
  show: inline-flex;
  align-items: middle;
  text-decoration: none;
  coloration: #28666e;
article a:focus {
  define: 1px dotted #28666e;
article a .icon {
  min-width: 24px;
  width: 24px;
  peak: 24px;
  margin-left: 5px;
  remodel: translateX(var(--link-icon-translate));
  opacity: var(--link-icon-opacity);
  transition: all 0.3s;

.article-body {
  padding: 24px;

Let’s be form to individuals and likewise add a display screen reader class hidden behind the hyperlink:

.sr-only:not(:focus):not(:energetic) {
  clip: rect(0 0 0 0); 
  clip-path: inset(50%);
  peak: 1px;
  overflow: hidden;
  place: absolute;
  white-space: nowrap; 
  width: 1px;

Our card is beginning to look fairly candy. It’s time so as to add a little bit of magic to it. With the :has() pseudo class, we are able to now examine if our hyperlink is hovered or targeted, then replace our customized properties and add a box-shadow. With this little chunk of CSS our card actually involves life:

/* Matches an article ingredient that accommodates a hover or focus state */
article:has(:hover, :focus) {
  --img-scale: 1.1;
  --title-color: #28666e;
  --link-icon-translate: 0;
  --link-icon-opacity: 1;

  box-shadow: rgba(0, 0, 0, 0.16) 0px 10px 36px 0px, rgba(0, 0, 0, 0.06) 0px 0px 0px 1px;

See what’s up there? Now we get the up to date types if any baby ingredient within the card is hovered or targeted. And despite the fact that the hyperlink ingredient is the one factor that may include a hover or focus state within the ::after clickable card strategy, we are able to use that to match the dad or mum ingredient and apply the transitions.

And there you may have it. Simply one other highly effective use case for the :has() selector. Not solely can we match a dad or mum ingredient by declaring different parts as arguments, however we are able to match additionally use pseudos to match and magnificence mother and father as properly.


  • Accessible
  • Animatable
  • No JavaScript wanted
  • Makes use of :hover on the right ingredient


  • Textual content is just not simply selectable.
  • Browser assist is proscribed to Chrome and Safari (it’s supported in Firefox behind a flag).

Here’s a demo utilizing this system. You would possibly discover an additional wrapper across the card, however that’s simply me taking part in round with container queries, which is simply a kind of different incredible issues rolling out in all main browsers.

Received some different examples you want to share? Different options or concepts are greater than welcome within the remark part.



Please enter your comment!
Please enter your name here

Most Popular

Recent Comments