For those times when you have a DOM element, but its children are responsible for how it looks ✨.
The Problem
As an example of this, we’ll work on a radio-button
group. The challenge is that each option has quite a bit of information ℹ️ inside, and based on that information, the radio-button
should look different. Hopefully, this will make more sense with a peek 👀 at the final result.
Good to know
Not much, this post relies solely on HTML and CSS knowledge 📚.
The Solution
We’ll split this into a couple of steps:
- Build the HTML of the component 🏗️
- Add some basic CSS styling 🎨
- The magic touch 🧙♂️
This time, I’ll try a different approach. Instead of showing big chunks of code 🧑💻 and explaining what all of it does, I will go bit by bit, with small, incremental changes. Please let me know what you think 🤔(you can use Twitter for example 👇).
Let’s go!
Part 1 - Build the HTML of the component 🏗️
Like I mentioned, this is a radio-button
group, so let’s start with that.
Here we have exactly that. Three radio-buttons
, all of them using the same name, meaning they all set/use the same value: audience
. They have unique IDs and unique values.
Each of them also has a label
that allows the user to click 👆 on the label OR the radio-button
to select the value.
All this results in:
Now, let’s add more details inside our label
.
Now, each label
has an icon, a title, and a description. Nothing special here. It’s ugly for now, but don’t worry about that yet 😉.
Here’s the updated result
Finally, to make this a bit more self-contained, let’s add a wrapper all around everything, and let’s add a label
for this audience
📣 field.
Now it’s all complete. HTML wise, at least. We have a fully encapsulated field, that has a label
and has a radio-group
input
for its value.
Here’s the final HTML result.
Part 2 - Add some CSS for styling 🎨
Ok, time to make this a bit prettier.
First, the wrapper. Let’s make sure the label
and the radio-button
group are side by side. Not a challenge 💪 for a flexbox
.
Pretty straightforward. This will make the wrapper a flexbox
, and put the 2 children side by side, with a space of 12px
in between.
Now, for the audience-options
. Let’s make the whole element prettier ✨ by adding some space.
Besides the flexbox
here, we’re also using some nested selectors. For your mental model 🧠, you can replace the &
with the selector that it sits inside of. So here, every &
is equivalent to .audience-options
. This way, & h6
becomes .audience-options h6
, which translates to a h6
inside an element with a class of audience-options
.
I hope this is clear. In this nested selector we adjust the spacing of the title.
Neeeext! 😁
Finally, let’s make each option-description
prettier ✨. Here’s what I ended up doing.
It’s not spectacular, but it’s better. Same thing here with the nesting. That should make sense now. The last thing that’s a bit different here is the :first-of-type
and :last-of-type
selector. They do exactly that. Select the first p
element in this case inside the .option-description
(because of the &
), and the last p
element inside the .option-description
.
Here’s another way of putting this:
& p:first-of-type
➡️.option-description p:first-of-type
➡️ select the firstp
element inside an element with theoption-description
class.
& p:last-of-type
➡️.option-description p:last-of-type
➡️ select the lastp
element inside an element with theoption-description
class.
With that out of the way, what we’re doing is using CSS Grid here to style each box. We want to make sure the emoji takes 2 rows, so it will be on the left side, and the title and the description on the right side.
This looks like a nice radio-button
group.
Part 3 - The magic touch 🧙♂️
Ok, now we want to actually show within the box, which box is selected, and not using the radio-button
circle thingy. And here lies the problem. We want to change the background of the option if, inside it, there’s a checked input
🤔.
✌️ :has
to the rescue! ⚔️
Turns out this is simple to achieve.
What do we have here? Well, we’re selecting the elements that have the audience-option
class, and inside, they HAVE an input
that is checked. Makes sense?
Now, if we look at our result, we can see that the selected box has a yellow background. With that achieved, we can now hide the radio-button
thingy.
We added the hiding of the input
here.
This looks delightful ✨ now!
Let’s do more things here. Let’s change cursors 👆, and let’s style the disabled inputs
as well. Here’s what I ended up with:
Nothing new here, just more usage of the :has
selector. Isn’t this 🆒?
Well, that’s about it. Go wild with this knowledge 📚 now!
Furthermore, here's a link to a working CodePen.
Congrats 🎉
You made it yet through another article. Thanks for spending the time 🕰️ reading this, and hope it provided some sort of value, since time is a limited resource. Use it wisely. 🙏
Want more?
If you want to know more about this, or something doesn't make any sense, please let me know.
Also, if you have questions, you know where to find me… On the internet!
Be kind to each other! 🧡
Over and out
📢 Check out my latest article on CSS tidbits! Discover how to style the parent element based on its children, unlocking endless possibilities for dynamic web design 🚀: https://catalincodes.com/posts/css-tidbits-how-to-style-parent-based-on-children #HTML #CSS #UI