frontend
Use CSS to Reveal a Child Element in the DOM by Interacting with the Parent
When you want to trigger a change in a nested component, just move the :hover higher.
Introduction
Building my own personal site was a lot of fun because I got to spend time on the little details and subtle touches to give it that little something extra - I know I always appreciate when I see them on other sites. One such detail that I wanted to add was a little arrow that appears right next to the "Read more" link on cards for each of my blog posts when a user hovers over it.
At first glance, it sounds like a relatively simple matter for the CSS :hover
selector, but what I actually wanted to happen was when a user hovered over any part of my card, the arrow would appear - not just the "Read more" link in the bottom left corner of the card. What I really wanted was for the larger parent container to trigger a CSS change in a child element. Getting this to work proved a little trickier to figure out how. But I persevered, and in the end, had a little arrow that appears and disappears depending on a user's mouse. It's an understated addition and I like it - almost like an Easter egg.
This article will show you how to use an interaction that a user takes on a parent element (like hovering over it) in the DOM to make a change in a child element via CSS. (I can see lots of situations where something like this could come in pretty handy.)
Below is a video showing how the arrow next to the "Read more" text on the card appears no matter where a user mouses within the card: title, body, tags - anywhere in the card makes the arrow appear, not just when a user mouses over the link itself.
Make a blog post card's HTML (or JSX)
Before we get to the CSS portion of this post, we first need to set up the general outline of a blog post card's HTML.
My site is built with React using the Gatsby framework. In my code examples, you'll see a smidge of React and JSX instead of traditional HTML, but the CSS is really where our focus will be for the majority of this article so don't worry too much about it.
NOTE: I've omitted most of the React-specific code like Hooks, state, functions, etc. to better focus the scope of this article, but if you want to see the full code, you can click the title of any of the code examples to see my full repo files in GitHub.
Here is a simplified look at the LatestPosts
component which fetches all the blog posts available on my site, and takes the most recent four to display in a list of cards.
// other imports for React, components, constants, etc.
import './LatestPosts.scss';
const LatestPosts = () => {
// state variables here
// logic to fetch all blog posts, take the most recent 4 posts and format the data to display
return (
<div className="latest-posts-wrapper">
{posts.length
? posts.map((post, index) => (
<div className="post" key={index}>
<Link
className="post-link"
to={`/blog${post.path}`}
key={post.title}
>
<p className="post-title">{post.title}</p>
<p className="post-sub-title">{post.subTitle}</p>
<PostTags tags={post.tags} />
<p className="read-more">
Read more <span className="arrow">></span>
</p>
</Link>
</div>
))
: null}
<h2>
<Link to="/blog">View all articles ></Link>
</h2>
</div>
)
};
export default LatestPosts;
In this component code, there's a single <div>
(as required by all React components) that contains all the posts displayed in this list: it's the <div>
with the latest-posts-wrapper
class.
Then, the list of posts
is iterated over with .map()
and each individual post is rendered as an element in the DOM that looks like a card. Each card has a link to the post, the blog title, subtitle, tags, and the text Read more
encouraging users to click through.
Take note that the <Link>
element wraps the majority of the blog post content - that will be key soon enough.
Inside of the final <p>
tag with the class of read-more
is a <span>
tag that surrounds the >
- this is the HTML for the greater than sign (>) which I'm using as a right arrow symbol.
Finally, there's another header tag at the bottom of the component, <h2>
, inviting users to go the site page with all the blog posts.
Ok, now that the HTML groundwork's been laid, let's get on with the CSS and making these arrows appear appear and disappear as required.
Add the CSS to the component
Now there's a bit more SCSS associated with the <LatestPosts>
component, but for clarity I've omitted the bits not relevant to the blog. If you'd like to see the unabridged version of the code, you can click the name of the file and see it in GitHub.
NOTE: The code below is actually SCSS instead of pure CSS because I greatly appreciate the additional features like class nesting that it provides, but it should be pretty simple for you to translate this as needed.
Ok, here's the CSS: now let's talk about what's happening.
.latest-posts-wrapper {
display: flex;
flex-direction: column;
.post {
margin: 10px 20px;
padding: 20px 30px;
background-color: white;
box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px;
border-radius: 5px;
.post-link {
// post-related css
.arrow {
opacity: 0;
}
}
.post-link:hover {
// more post-related css
.arrow {
opacity: 1;
}
}
}
}
As you look at this code, take special note of the classes .post-link
and .arrow
- they're the keys here.
.post-link
is the class that surrounds all the content within each card. arrow
is the class that goes with the greater than arrow.
In order to keep the arrow hidden from view until a user hovers over the card, we'll set the arrow
class to opacity: 0;
inside of the post-link
class. Even though it's still there in the HTML, it's not visible onscreen.
Then, when a user hovers over a card, we actually invoke the CSS pseudo class :hover
on the post-link
class and switch the arrow
class' opacity to opacity: 1;
, and that makes the arrow show up as soon as a mouse enters anywhere inside the card.
Instead of adding the :hover
to the read-more
class that would only make the arrow show when a user mouses over the "Read more" link, we add the :hover
higher up in the card element which makes the arrow show up almost anywhere the user's mouse is on the card. Voila!
And that's all there is to it: simply shifting the :hover
pseudo class that surrounds the child element further up in the larger parent component can make it happen.
Once more, here's what the finished product looks like: hover almost anywhere on the card the arrow appears, mouse off of it, the arrow disappears again.
Conclusion
One of the coolest parts of being a web developer is bringing things to life online, and even though I am not a designer, it was really fun to flex my design muscles as I built my own site, and add all sorts of little touches that I liked.
Things like gradients here and there, subtle animations, and the like: nothing too dramatic mind you, but small things that I always appreciate when I see it on other sites.
Not too long ago, a touch I wanted to add was when a user moused over one of my blog cards they'd see a little arrow next to the "Read more" prompt pop up. But I wanted the arrow to pop up anywhere a mouse might hover over the card - not just when they got the "Read more" link in the bottom of the card.
By employing the :hover
pseudo-class on one of the card's classes that wraps all of the card's content (and not just the "Read more" link), I was able to achieve this effect and it didn't take very much effort at all. Pretty sweet.
Check back in a few weeks — I’ll be writing more about JavaScript, React, IoT, or something else related to web development.
Thanks for reading. I hope this trick to affect a child element's CSS by interacting with a parent or grandparent element comes in as handy for you as it does for me.
References & Further Resources
Want to be notified first when I publish new content? Subscribe to my newsletter.