Conditionally Applying CSS Styles Based on Element Count
Have you ever needed to apply different styles based on the number of elements that exist in a particular group? Some developers might immediately turn to JavaScript in order to count the number of HTML elements. Then they might conditionally add or remove a class in order to apply different styles. However, JavaScript isn t actually needed, and in this post I ll show you how you can leverage CSS to accomplish this task.
Let s say you have some very specific business requirements for styling a row of items. They might look like the following:
-
If two items exist, apply 50% width to each.
-
If three items exist, apply 50% width to the first two items, and 100% width to the third item.
-
If four items exist, apply 25% width to each.
While this is trivial to implement using JavaScript, a CSS-only solution would be much cleaner. As a general rule of thumb - I try to avoid using JavaScript for anything that can be handled via CSS. This encourages a separation of concerns and helps keep code performant.
The CSS-only solution involves combining and leveraging the nth-child
and nth-last-child
pseudo class selectors. This will allow us to conditionally select elements when only a certain number are present . For example, to solve the first business requirement we could use the following selectors:
// if two items exist, apply 50% width
.item:nth-child(1):nth-last-child(2),
.item:nth-child(2):nth-last-child(1) {
width: 50%;
}
By counting an element s position from both the start and the end of the row, we can conditionally apply styles when only a certain number of elements are present. So if an element is the first item from the beginning of the row and the second item from the end of the row - that means there s only two items present.
Using the same logic, we can solve the remaining business requirements with the following code:
// two items = 50% width
.item:nth-child(1):nth-last-child(2),
.item:nth-child(2):nth-last-child(1) {
width: 50%;
}
// three items = 50% width
// ... except for last item, which has 100% width
.item:nth-child(1):nth-last-child(3),
.item:nth-child(2):nth-last-child(2) {
width: 50%;
}
.item:nth-child(3):nth-last-child(1) {
width: 100%;
}
// four items = 25% width
.item:nth-child(1):nth-last-child(4),
.item:nth-child(2):nth-last-child(3),
.item:nth-child(3):nth-last-child(2),
.item:nth-child(4):nth-last-child(1) {
width: 25%;
}
Now I admit, this can become tedious to write out over time. This is where CSS preprocessors such as Sass come in handy.
Creating a Sass Mixin
You may have noticed a pattern emerging here, especially in the last example. For each selector string the nth-child
argument is incrementing by one, while the nth-last-child
argument is decrementing by one. We can easily duplicate this logic pattern via a Sass mixin:
@mixin if($args...) {
@each $arg in $args {
@if type-of($arg) == number {
@for $i from 1 through $arg {
&:nth-child(#{$i}):nth-last-child(#{$arg - $i + 1}) {
@content;
}
}
}
}
}
And then include the mixin like so:
.item {
@include if(2) {
width: 50%;
}
@include if(3) {
width: 50%;
&:last-child {
width: 100%;
}
}
@include if(4) {
width: 25%;
}
}
Also note that you can pass multiple arguments to the if
mixin. For example:
.item {
// if 1 or 2 items exist
@include if(1, 2) {
width: 50%;
}
}
Now you have a nifty if
mixin to add to your toolbelt, which could come in handy when dealing with complex styling requirements.