close
Skip to content

Tutorial on building a "hover reveal" pattern #215

@bph

Description

@bph

Discussed in #203

Originally posted by justintadlock January 11, 2024
There's been a lot of discussion around building a specific layout with the block editor. Specifically, something like this (shown in a hover card style):

image

This is a pretty popular web design pattern, and it'd be great to show how it's possible with the block editor and can be shipped in a theme as a pattern.

Some of the community discussion and responses:

Responses to my tweet (last link above) have been overwhelmingly positive and ask for a tutorial. Thus, this proposal.

The Proposal

Write a tutorial that covers a lot of ground that is geared toward theme creators. This would be:

  • How to build this layout in the WordPress editor (no custom CSS or plugins necessary!).
  • How to ship this layout in a block pattern.
  • How to make this pattern even more awesome with custom block styles:
    • Add a "hover reveal" block style for the Cover block.
    • Add a "flip card" block style for the Cover block (haven't tested this yet but am pretty sure it'll work).
    • Add a "grid auto" option for improved Columns block responsiveness.

The Code

I've done some preliminary code work and am just backing it up here. Note: some of the below will have classes and such from my personal theme, so it'll need to be translated to TT4.

assets/css/blocks/core/columns.scss:

// Grid style.
.wp-block-columns-is-layout-flex.is-style-grid-auto {
	display: grid !important;
	grid-template-columns: repeat( 1, 1fr );

	@media ( min-width: 40rem ) {
		grid-template-columns: repeat( 2, 1fr );
	}

	@media ( min-width: 64rem ) {
		&:has( > :where(
			.wp-block-column:nth-child( 5 ),
			.wp-block-column:nth-child( 6 )
		) ) {
			grid-template-columns: repeat( 3, 1fr );
		}
	}

	@media ( min-width: 80rem ) {
		grid-auto-columns: minmax( 0, 1fr );
		grid-auto-flow: column;
	}
}

assets/css/blocks/core/cover.scss:

// Hover reveal block style.
.wp-block-cover.is-style-hover-reveal {
	position: relative;
	border-radius: var( --wp--custom--defaults--border-radius );
	box-shadow: var( --wp--custom--defaults--shadow );

	// The cover background (used for the overlay), needs to have its
	// opacity set to `1` so that we have a background when the image is
	// hidden for the reveal.
	.wp-block-cover__background {
		transition: opacity 0.5s ease-in-out;
	}

	// Only set the opacity if we're not hovered/focused.
	&:where( :not( :hover, :focus-within ) ) .wp-block-cover__background {
		opacity: 1;
	}

	// By default, we zero out the background image's opacity. Then, we
	// reveal it on hover/focus.
	.wp-block-cover__image-background {
		opacity: 0;
		transition: all 0.5s ease-in-out;
	}

	&:hover,
	&:focus-within {
		.wp-block-cover__image-background {
			opacity: 1;
			transform: scale( 1.3 );
		}
	}

	// Remove text decoration for links.
	a {
		text-decoration: none;
		outline: none;
	}

	// This will technically take all links and position them over the
	// entire Cover block container. However, the last link will win out.
	// We're also making sure that we don't target this when in the editor.
	&:where( :not( [class*=block-editor] ) ) a:after {
		content: "";
		z-index: 1;
		position: absolute;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
		width: 100%;
		height: 100%;
	}
}

patterns/hero-cards.php:

<?php
/**
 * Title: Hero: Hover Cards
 * Slug: x3p0-ideas/hero-hover-cards
 * Description: Make a statement.
 * Categories: featured
 * Keywords: hero, cover, hover, card
 * Block Types: core/cover
 * Viewport Width: 1376
 */
$url = home_url();
$image = get_theme_file_uri( 'public/media/images/purple-sunset.webp' );
?>
<!-- wp:group {
	"align":"full",
	"style":{
		"spacing":{
			"padding":{
				"top":"var:preset|spacing|plus-4",
				"bottom":"var:preset|spacing|plus-4",
				"left":"var:preset|spacing|plus-3",
				"right":"var:preset|spacing|plus-3"
			},
			"blockGap":"var:preset|spacing|plus-4"
		}
	},
	"backgroundColor":"neutral-base",
	"layout":{"type":"default"}
} -->
<div class="wp-block-group alignfull has-neutral-base-background-color has-background" style="padding-top:var(--wp--preset--spacing--plus-4);padding-right:var(--wp--preset--spacing--plus-3);padding-bottom:var(--wp--preset--spacing--plus-4);padding-left:var(--wp--preset--spacing--plus-3)">

	<!-- wp:group {
		"style":{"spacing":{"blockGap":"var:preset|spacing|base"}},
		"layout":{"type":"constrained"}
	} -->
	<div class="wp-block-group">

		<!-- wp:group {
			"style":{"spacing":{"blockGap":"var:preset|spacing|minus-3"}},
			"layout":{"type":"constrained"}
		} -->
		<div class="wp-block-group">

			<!-- wp:paragraph {"align":"center"} -->
			<p class="has-text-align-center"><?php esc_html_e( 'Placeholder Text', 'x3p0-ideas' ) ?></p>
			<!-- /wp:paragraph -->

			<!-- wp:heading {"textAlign":"center"} -->
			<h2 class="wp-block-heading has-text-align-center"><?php esc_html_e( 'Placeholder Heading', 'x3p0-ideas' ) ?></h2>
			<!-- /wp:heading -->

		</div>
		<!-- /wp:group -->

		<!-- wp:paragraph {"align":"center"} -->
		<p class="has-text-align-center"><?php esc_html_e( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam interdum turpis quis metus aliquam, id pharetra arcu dignissim. Sed in sollicitudin mi.', 'x3p0-ideas' ) ?></p>
		<!-- /wp:paragraph -->

	</div>
	<!-- /wp:group -->

	<!-- wp:columns {"className":"is-style-grid-auto"} -->
	<div class="wp-block-columns is-style-grid-auto">

		<?php foreach ( range( 1, 4 ) as $column ) : ?>

			<!-- wp:column -->
			<div class="wp-block-column">

				<!-- wp:cover {
					"url":"<?= esc_url( $image ) ?>",
					"id":2329,
					"dimRatio":50,
					"overlayColor":"contrast",
					"isUserOverlayColor":true,
					"minHeight":20,
					"minHeightUnit":"vh",
					"style":{
						"spacing":{
							"padding":{
								"top":"var:preset|spacing|plus-3",
								"bottom":"var:preset|spacing|plus-3",
								"left":"var:preset|spacing|plus-3",
								"right":"var:preset|spacing|plus-3"
							}
						}
					},
					"className":"is-style-hover-reveal",
					"layout":{"type":"default"}
				} -->
				<div class="wp-block-cover is-style-hover-reveal" style="padding-top:var(--wp--preset--spacing--plus-3);padding-right:var(--wp--preset--spacing--plus-3);padding-bottom:var(--wp--preset--spacing--plus-3);padding-left:var(--wp--preset--spacing--plus-3);min-height:20vh">

					<span aria-hidden="true" class="wp-block-cover__background has-contrast-background-color has-background-dim"></span>
					<img class="wp-block-cover__image-background wp-image-2329" alt="" src="<?= esc_url( $image ) ?>" data-object-fit="cover"/>

					<div class="wp-block-cover__inner-container">

						<!-- wp:group {
							"align":"full",
							"style":{"dimensions":{"minHeight":"24rem"}},
							"layout":{
								"type":"flex",
								"orientation":"vertical",
								"verticalAlignment":"space-between"
							}
						} -->
						<div class="wp-block-group alignfull" style="min-height:24rem">

							<!-- wp:group {
								"style":{
									"spacing":{
										"padding":{
											"top":"var:preset|spacing|minus-3",
											"bottom":"var:preset|spacing|minus-3",
											"left":"var:preset|spacing|base",
											"right":"var:preset|spacing|base"
										}
									}
								},
								"backgroundColor":"primary-contrast",
								"layout":{"type":"default"},
								"fontSize":"sm"
							} -->
							<div class="wp-block-group has-primary-contrast-background-color has-background has-sm-font-size" style="padding-top:var(--wp--preset--spacing--minus-3);padding-right:var(--wp--preset--spacing--base);padding-bottom:var(--wp--preset--spacing--minus-3);padding-left:var(--wp--preset--spacing--base)">

								<!-- wp:paragraph -->
								<p><?php esc_html_e( 'Placeholder', 'x3p0-ideas' ) ?></p>
								<!-- /wp:paragraph -->

							</div>
							<!-- /wp:group -->

							<!-- wp:group {
								"style":{
									"spacing":{"blockGap":"var:preset|spacing|minus-3"}},
									"layout":{"type":"default"}
								} -->
							<div class="wp-block-group">

								<!-- wp:heading {"level":3} -->
								<h3 class="wp-block-heading"><a href="<?= esc_url( $url ) ?>"><?php esc_html_e( 'Placeholder Heading', 'x3p0-ideas' ) ?></a></h3>
								<!-- /wp:heading -->

								<!-- wp:paragraph {"fontSize":"sm"} -->
								<p class="has-sm-font-size"><?php esc_html_e( 'This is placeholder text.', 'x3p0-ideas' ) ?></p>
								<!-- /wp:paragraph -->

							</div>
							<!-- /wp:group -->

						</div>
						<!-- /wp:group -->

					</div>

				</div>
				<!-- /wp:cover -->

			</div>
			<!-- /wp:column -->

		<?php endforeach ?>

	</div>
	<!-- /wp:columns -->

</div>
<!-- /wp:group -->

```</div>

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

Status

Published (Done)

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions