Clickable cards are a common interactive component. There are quite a few things to consider when creating them, and a lot depends on the context in which they’re being placed. Today, you will learn how to create them with the Grid block and add some CSS-based animation to enhance the final outcome.
Here are the general steps you will take:
- Create a layout with the Grid block and nested inner blocks.
- Register a new block style to expand the clickable surface area.
- Add animation to nested elements with more custom styles.
- Save the block layout as a Pattern.
Below is a video that captures the final “hover reveal” effect that you’ll create. If you would like to experiment with the final pattern or even a plugin while reading, here is the Grid Cards pattern and the hover-reveal-effect plugin.
Table of Contents
Create the card layout with the Grid block
First, you’ll build the card layout and appearance with blocks. I ended up using the following blocks:
- A Grid block with Auto layout type enabled. Also, you may want to consider setting the Advanced > HTML Element to
<section>
.- A Cover block with an image and an overlay applied. Also, you may want to consider setting the Advanced > HTML Element to
<article>
. This depends on how you intend to use the final pattern. Different HTML semantics can apply in different contexts.- A Stack block with a minimum height of
22rem
and the vertical alignment type is set to Space between. These blocks are representative of our cards.- A Group block with a nested Paragraph block to represent a label for our card.
- Another Group block comprised of a nested Heading with a link and Paragraph block as a brief excerpt.
- A Stack block with a minimum height of
- A Cover block with an image and an overlay applied. Also, you may want to consider setting the Advanced > HTML Element to
Below is a screenshot of the blocks that form the final Grid Cards pattern:
You can experiment with different spacing, colors, and typography so that the inner blocks match your overall design aesthetic.
Bonus: Block renaming in list view
Consider renaming the key blocks in the List View. This is a nice touch for enhancing the editorial experience and can help clarify what the nested elements represent in each card. Once you’ve finalized your overall block configuration and want to save it as a pattern then the names will save as well.
Here is a screenshot which shows how you might rename your cards blocks.
Increase the clickable surface area
You might have noticed that only the Heading block is clickable. However, your site’s visitors—especially those browsing on mobile—may appreciate a larger clickable surface area.
Register a block style for a clickable card
WordPress doesn’t offer a native method to create such interactions. To achieve that, you would need to register a block style and add custom CSS. Add the following code to your theme’s functions.php
:
add_action( 'init', 'themeslug_register_block_styles' );
function themeslug_register_block_styles() {
register_block_style(
'core/cover',
array(
'name' => 'card--interactive',
'label' => __( 'Card (Interactive)', 'themeslug' ),
'inline_style' => '
.is-style-card--interactive {
position: relative;
}
.is-style-card--interactive :where(.wp-block-group.wp-block-group-is-layout-constrained) {
position: static;
}
.is-style-card--interactive :where(.wp-block-heading) a:after {
content: "";
inset: 0;
position: absolute;
z-index: 10;
}
',
)
);
}
The code targets the Cover block with custom CSS and expects a nested Heading block that contains a link.
Back in the Editor, click each Cover block, assign the “Cards (Interactive)” style, and verify that the entire card is clickable.
If you’re happy with the results, you could stop here and enjoy this fully clickable card. Or, you could add a bit of pizzazz with some animation of the inner blocks.
Animate the label and the excerpt
Subtle animation can make for a delightful experience. You can extend your custom “Card (Interactive)” block with some CSS styles.
To keep things organized, let’s move the styles into a separate file and update the functions.php
file:
- Create a new file within your theme:
assets/css/blocks/core/cover–card-interactive.css
. - Update the
register_block_style()
function and enqueue this new stylesheet. Instead of theinline_style
property, switch to thestyle_handle
property and pass a registered handle that calls the external stylesheet.
Replace the previous code with the following:
add_action( 'init', 'themeslug_enqueue_block_styles' );
function themeslug_enqueue_block_styles() {
wp_enqueue_block_style(
'core/cover',
array(
'handle' => 'themeslug--card-interactive',
'src' => get_theme_file_uri( 'assets/css/blocks/core/cover-card-interactive.css' ),
'path' => get_theme_file_path( 'assets/css/blocks/core/cover-card-interactive.css' ),
)
);
}
add_action( 'init', 'themeslug_register_block_styles' );
function themeslug_register_block_styles() {
register_block_style(
'core/cover',
array(
'name' => 'card--interactive',
'label' => __( 'Card (Interactive)', 'themeslug' ),
'style_handle' => 'themeslug--card-interactive',
)
);
}
Copy the following CSS into your cover-card-interactive.css
file. You’re welcome to adjust the animations to your liking:
.is-style-card--interactive {
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(0, 0, 0, 0.1), 0 4px 4px rgba(0, 0, 0, 0.1), 0 8px 8px rgba(0, 0, 0, 0.1), 0 16px 16px rgba(0, 0, 0, 0.1);
position: relative;
transition: box-shadow 0.5s ease;
}
.is-style-card--interactive:focus-within,
.is-style-card--interactive:hover {
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 2px 2px rgba(0, 0, 0, 0.2), 0 4px 4px rgba(0, 0, 0, 0.2), 0 8px 8px rgba(0, 0, 0, 0.2), 0 16px 16px rgba(0, 0, 0, 0.2);
}
.is-style-card--interactive :where(.wp-block-group.wp-block-group-is-layout-constrained) {
position: static;
}
/* Make whole card clickable */
.is-style-card--interactive:not(.has-child-selected) :where(.wp-block-heading) a:after {
content: "";
inset: 0;
position: absolute;
z-index: 10;
}
/* Animate the Cover block image */
.is-style-card--interactive :where(.wp-block-cover__image-background) {
filter: saturate(100%) brightness(100%);
transform: scale(1);
transition: all 0.35s ease;
}
.is-style-card--interactive:not(.has-child-selected):focus-within :where(.wp-block-cover__image-background),
.is-style-card--interactive:not(.has-child-selected):hover :where(.wp-block-cover__image-background) {
filter: saturate(200%) brightness(40%);
transform: scale(1.15);
}
/* Animate label area */
.is-style-card--interactive :where(.is-vertical) .wp-block-group:first-of-type {
opacity: 0;
transform: scale(0.95) translateX(-1rem);
transform-origin: center right;
transition: all 0.25s ease-in-out;
transition-delay: 0.2s;
}
.is-style-card--interactive:focus-within :where(.is-vertical) .wp-block-group:first-of-type,
.is-style-card--interactive:hover :where(.is-vertical) .wp-block-group:first-of-type {
opacity: 1;
transform: scale(1) translateX(0);
}
/* Animate content area */
.is-style-card--interactive:not(.has-child-selected) :where(.is-vertical) .wp-block-group:first-of-type + .wp-block-group p {
max-height: 0;
opacity: 0;
overflow: hidden;
transition: max-height 0.35s cubic-bezier(.19,1,.22,1), opacity 0.6s ease;
}
.is-style-card--interactive:focus-within :where(.is-vertical) .wp-block-group:first-of-type + .wp-block-group p,
.is-style-card--interactive:hover :where(.is-vertical) .wp-block-group:first-of-type + .wp-block-group p {
max-height: 100%;
opacity: 1;
}
.is-style-card--interactive :where(.is-vertical) {
display: flex;
}
@media (prefers-reduced-motion: reduce) {
.is-style-card--interactive *,
.is-style-card--interactive *::after,
.is-style-card--interactive *::before {
opacity: 1 !important;
transition: none !important;
visibility: visible !important;
}
}
Wrapping up
With a little planning and thought, you can progressively enhance your site’s interactivity and elevate the overall experience for your visitors.
You can find the complete plugin code in this GitHub repository. You’re welcome to fork it, customize it, and share how you use it in projects in the comments below.
Props to @greenshady, @ironnysh, and @bph for the thoughtful feedback and review of this post.
Leave a Reply