Tag: Cards

Creating Animated, Clickable Cards With the :has() Relational Pseudo Class

The CSS :has() pseudo class is rolling out in many browsers with Chrome and Safari already fully supporting it. It’s often referred to it as “the parent selector” — as in, we can select style a parent element from a child selector — but there is so much more that :has() can help us solve. One of those things is re-inventing the clickable card pattern many of us love to use from time to time.

We’ll take a look at how :has() can help us handle linked cards, but first…

What is this :has() pseudo class?

There is already a bunch of great posts floating around that do an excellent job explaining what :has() is and what it’s used for, but it’s still new enough that we ought to say a few words about it here as well.

:has() is a relational pseudo class that’s part of the W3C Selectors Level 4 working draft. That’s what the parentheses are all about: matching elements that are related to — or, more accurately, contain — certain child elements.

/* Matches an article element that contains an image element */ article:has(img) { }  /* Matches an article element with an image contained immediately within it */ article:has(> img) { }

So, you can see why we might want to call it a “parent” selector. But we can also combine it with other functional pseudo classes to get more specific. Say we want to style articles that do not contain any images. We can combine the relational powers of :has() with the negation powers of :not() to do that:

/* Matches an article without images  */ article:has(:not(img)) { }

But that’s just the start of how we can combine powers to do more with :has(). Before we turn specifically to solving the clickable card conundrum, let’s look at a few ways we currently approach them without using :has().

How we currently handle clickable cards

There are three main approaches on how people create a fully clickable card these days and to fully understand the power of this pseudo class, it’s nice to have a bit of a round-up.

This approach is something used quite frequently. I never use this approach but I created a quick demo to demonstrate it:

There are a lot of concerns here, especially when it comes to accessibility. When users navigate your website using the rotor function, they will hear the full text inside of that <a> element — the heading, the text, and the link. Someone might not want to sit through all that. We can do better. Since HTML5, we can nest block elements inside of an <a> element. But it never feels right to me, especially for this reason.

Pros:

  • Quick to implement
  • Semantically correct

Cons:

  • Accessibility concerns
  • Text not selectable
  • A lot of hassle to overwrite styles that you used on your default links

The JavaScript method

Using JavaScript, we can attach a link to our card instead of writing it in the markup. I found this great CodePen demo by costdev who also made the card text selectable in the process:

This approach has a lot of benefits. Our links are accessible on focus and we can even select text. But there are some drawbacks when it comes to styling. If we want to animate those cards, for example, we would have to add :hover styles on our main .card wrapper instead of the link itself. We also would not benefit from the animations when the links are in focus from keyboard tabbing.

Pros:

  • Can be made perfectly accessible
  • Ability to select text

Cons:

  • Requires JavaScript
  • Right clicking not possible (although could be fixed with some extra scripting)
  • Will require a lot of styling on the card itself which would not work when focussing the link

The ::after selector approach

This method requires us to set the card with relative positioning, then set absolute positioning on the link’s ::after pseudo selector of a link. This doesn’t require any JavaScript and is pretty easy to implement:

There are a few drawbacks here, especially when it comes to selecting text. Unless you provide a higher z-index on your card-body, you won’t be able to select text but if you do, be warned that clicking the text will not activate your link. Whether or not you want selectable text is up to you. I think it can be a UX issue, but it depends on the use-case. The text is still accessible to screen readers but my main problem with the method is the lack of animation possibilities.

Pros:

  • Easy to implement
  • Accessible link without bloated text
  • Works on hover and focus

Cons:

  • Text is not selectable
  • You can only animate the link as this is the element you’re hovering.

A new approach: Using ::after with :has()

Now that we’ve established the existing approaches for clickable cards, I want to show how introducing :has() to the mix solves most of those shortcomings.

In fact, let’s base this approach on the last one we looked at using ::after on the link element. We can actually use :has() there to overcome that approach’s animation constraints.

Let’s start with the markup:

<div class="card">   <img src="cat.webp" alt="Fluffy gray 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>   </div> </div>

I will be keeping things as simple as possible by targeting elements in the CSS instead of classes.

For this demo, we’re going to add an image zoom and shadow to the card on hover, and animate the link with an arrow popping up and while changing the link’s text color. To make this easy, we’re going to add some custom properties scoped on our card. Here’s the basic styling:

/* The card element */ 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: #fff;   transform-origin: center;   transition: all 0.4s ease-in-out;   overflow: hidden; } /* The link's ::after pseudo */ article a::after {   content: "";   position: absolute;   inset-block: 0;   inset-inline: 0;   cursor: pointer; }

Great! We added an initial scale for the image (--img-scale: 1.001), the initial color of the card heading (--title-color: black) and some extra properties we will use to make our arrow pop out of the link. We’ve also set an empty state of the box-shadow declaration in order to animate it later . This sets up what we need for the clickable card right now, so let’s add some resets and styling to it by adding those custom properties to the elements we want to animate:

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

Let’s be kind to people and also add a screen reader class hidden behind the link:

.sr-only:not(:focus):not(:active) {   clip: rect(0 0 0 0);    clip-path: inset(50%);   height: 1px;   overflow: hidden;   position: absolute;   white-space: nowrap;    width: 1px; }

Our card is starting to look pretty sweet. It’s time to add a bit of magic to it. With the :has() pseudo class, we can now check if our link is hovered or focused, then update our custom properties and add a box-shadow. With this little chunk of CSS our card really comes to life:

/* Matches an article element that contains 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 updated styles if any child element in the card is hovered or focused. And even though the link element is the only thing that can contain a hover or focus state in the ::after clickable card approach, we can use that to match the parent element and apply the transitions.

And there you have it. Just another powerful use case for the :has() selector. Not only can we match a parent element by declaring other elements as arguments, but we can match also use pseudos to match and style parents as well.

Pros:

  • Accessible
  • Animatable
  • No JavaScript needed
  • Uses :hover on the correct element

Cons:

  • Text is not easily selectable.
  • Browser support is limited to Chrome and Safari (it’s supported in Firefox behind a flag).

Here is a demo using this technique. You might notice an extra wrapper around the card, but that’s just me playing around with container queries, which is just one of those other fantastic things rolling out in all major browsers.

Got some other examples you wish to share? Other solutions or ideas are more than welcome in the comment section.


Creating Animated, Clickable Cards With the :has() Relational Pseudo Class originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

CSS-Tricks

, , , , , ,

WooCommerce Payments, Now with Support for Subscriptions and Saved Cards

A little while back we shared the news that WooCommerce shipped a beta payments feature as part of its 4.0 release. It’s a free plugin with no monthly costs or setup fees. You only pay when you make a sale.

We’re actually using this right here at CSS-Tricks. In fact, Chris blogged it back in July. Back then, we were using the WooCommerce Payments beta so we could start selling memberships here on the site and do it while taking payments without anyone having to leave the site to complete the transaction with a third-party.

The big news now is that WooCommerce Payments now supports WooCommerce Subscriptions. This is game-changer. It means you can offer a recurring payment option on subscription-based products and have all of those payments integrated with WooCommerce Payments reporting and features.

WooCommerce Payments works alongside other payment methods! We’ve enabled PayPal for anyone who prefers paying that way.

Enter subscriptions

The thing that makes WooCommerce Subscriptions such a great extension is that it turns any WooCommerce product into a possible subscription. So, yes, even a t-shirt can generate recurring payments (a shirt is not a good example of a subscription product, but the point is that subscriptions can be tied to anything). Anything you want to renew after a period of time is fair game. That could be a publication subscription that renews annually, a record of the month club with a monthly subscription, or even a payment plan that allows customers to pay for large purchases in monthly installments.

No no, the poster is not a subscription… but you can buy it with a one-time payment!

Now that WooCommerce Payments supports WooCommerce Subscriptions, not only are recurring payments a thing, but it brings all of those transactions to your store’s dashboard, making it a cinch to track those payments, as well as your cash flow. Payment disputes can even be handled without ever having to leave WordPress.

Oh, and saved credit cards!

In addition to subscriptions, WooCommerce Payments also supports saved credit cards. So now, when someone purchases anything on your site — whether it’s a single product or a recurring subscription — they can choose to save their payment information with you for faster transactions on future purchases!

Heck yeah, checking out next time will be a breeze!

All the things, all on WordPress

WooCommerce has been great for a long time, but it’s these sorts of enhancements that make it not just a killer experience but makes powerful e-commerce capabilities open to big and small stores alike. Get started with WooCommerce and WooCommerce Payments — it’s totally free to pick up and try out.


The post WooCommerce Payments, Now with Support for Subscriptions and Saved Cards appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Stacked Cards with Sticky Positioning and a Dash of Sass

The other day, I spotted this particularly lovely bit from Corey Ginnivan’s website where a collection of cards stack on top of one another as you scroll.

I started wondering how much JavaScript this would involve and how you’d go about making it when I realized — ah! — this must be the work of position: sticky and a tiny amount of Sass. So, without diving into how Corey did this, I decided to take a crack at it myself.

First up, some default styles for the cards:

body {   background: linear-gradient(#e8e8e8, #e0e0e0); }  .wrapper {   margin: 0 auto;   max-width: 700px; }  .card {   background-color: #fff;   border: 1px solid #ccc;   border-radius: 10px;   box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.1);   color: #333;   padding: 40px; }

Next, we need to make each card sticky to the top of the wrapper. We can do that like this:

.card {   position: sticky;   top: 10px;   // other card styles }

And that leaves us with this:

But how do we get each of these elements to look like a stack on top of one another? Well, we can use some fancy Sass magic to fix the position of each card. First we’ll loop over every card element and then change the value with each iteration:

@for $ i from 1 through 8 {   .card:nth-child(#{$ i}n) {     top: $ i * 20px;   } }

Which results in this demo, which is totally charming, if I do say so myself:

And there we have it! We could make a few visual changes here to improve things. For example, the box-shadow and color of each card, just like Corey’s example. But I wanted to keep experimenting here. What if we switch the order of the cards and made them horizontal instead?

We already do that on this very website:

After experimenting for a little bit I changed the order of the cards with flexbox and made each item slide in from right to left:

.wrapper {   display: flex;   overflow-x: scroll; }  .card {   height: 60vh;   min-width: 50vw;   position: sticky;   top: 5vh;   left: 10vw; }

But I also wanted to make each of the cards come in at different angles so I updated the Sass loop with the random function:

@for $ i from 1 through 8 {   .card:nth-child(#{$ i}n) {     left: $ i * 20px;     left: random(200) + $ i * 1px;     top: random(130) + $ i * 1px;     transform: rotate(random(3) - 2 * 1deg);   } }

That’s the bulk of the changes and that results in the following:

Pretty neat, eh? I love position: sticky; so much.


The post Stacked Cards with Sticky Positioning and a Dash of Sass appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Social Cards as a Service

I love the idea of programmatically generated images. That power is close at hand these days for us front-end developers, thanks to the concept of headless browsers. Take Puppeteer, the library for controlling headless Chrome. Generating images from URLs is their default use case:

const puppeteer = require('puppeteer');  (async () => {   const browser = await puppeteer.launch();   const page = await browser.newPage();   await page.goto('https://example.com');   await page.screenshot({path: 'example.png'});    await browser.close(); })();

That ought to get the ol’ mind grape going. What if we had URLs on our site that — with the power of our HTML and CSS skills — created perfect little designs for sharing using dynamic data… then turned them into images and used them for our meta tags?

The first I saw of this idea was Drew McLellan’s Dynamic Social Sharing Images. Drew wrote a script to fire up Puppeteer and get the job done.

Since the design part is entirely an HTML/CSS adventure, I’m sure you could imagine a setup where the URL passed in parameters that did things like set copy and typography, colors, sizes, etc. Zeit built exactly that!

The URL is like this:

https://og-image.now.sh/I%20am%20Chris%20and%20I%20am%20**cool**%20la%20tee%20ding%20dong%20da..png?theme=light&md=1&fontSize=100px&images=https%3A%2F%2Fassets.zeit.co%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fhyper-color-logo.svg

Kind of amazing that you can spin up an entire browser in a cloud function! Netlify also offers cloud functions, and when I mentioned this to Phil Hawksworth, he told me he was already doing this for his blog!

So on a blog post like this one, an image like this is automatically generated:

Which is inserted as meta:

<meta property="og:image" content="https://www.hawksworx.com/card-image/-blog-find-that-at-card.png">

I dug through Phil’s repos, naturally, and found his little machine for doing it.

I’m madly envious of all this and need to get one set up for myself.

The post Social Cards as a Service appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]