The alternative title of the article is “almost: child-count (n)”. Because that's how it all works. On bare CSS and without any data attributes or anything else in the layout.
Imagine that you have, for example, some news feed. No matter what. The main thing is that you do not know how many elements there will be in it, and how to arrange them so that it is symmetrical. And I want to do something useless, but beautiful: for example, put everything in two columns, and insert some blocks in full width. Every third, or every fifth.
Of course, if you have four elements, and you have made the third full width, the last one will hang at the end. Therefore, you need to apply such a beautiful and useless thing only if the number of elements is a multiple of three. And if there is an odd number of them (but not a multiple of three) - for example, the last element in the entire tire must be done.
So, for example:
')

Or, for example, you decided to make a radio, as in GTA 5. Here it is:

And you want to place the elements in a circle, but you do not know how many of them. Of course, for different cases - you need to specify a different transform: rotate (). Well, or, if desired, you can arrange everything through the left and top, relying on sin and cos. But still, you need to know how many items you have.
In the project, where it was necessary to implement just such a feature, I first did everything through the simplest js:
function countElementChildren(element) { $(element).attr('children', $(element).children().length) }
And in scss worked directly with
@for $i from 1 through 20 { .parent[children="#{$i}"] { @for $j from 1 through $i { & > :nth-child(#{$j}) { transform: rotate(360deg * (($j - 1) / ($i - 1))); } } } }
I hope this code is clear to everyone. If not, it unfolds in a construct of the form .parent [children = 2]>: nth-child (0) {transform: rotate (0deg)} and further along the cycleIt was possible to wrap up a call to this function in MutationObserver to keep track of the number of descendants, but still there was a feeling that this decision was wrong — it was just a style that did not relate to the main logic.
Probably, every person who has been engaged in styles for quite a long time, somewhere on the level of intuition, a detector appears, “can it be done on the same styles”.
As a result, almost a day the search for a solution was going on, how can the number of elements be determined? There were even two solutions, though very similar.
The logic was quite simple, technically we can refer to almost any element forward (through ~ and +), but to determine the number of elements in the list, you must be able to go ahead, count all the elements and go back.
"Reverse" movement is possible with just two attributes - nth-last-of-type and nth-last-child.
At the same time, this movement is actually performed from the last element, so that in the end you need to go through all the elements from the first to the last through nth-last-child and find the "final" element through the first-child
As a result, the first version of the selector was
:nth-last-child(20):first-child { }
which allowed us to refer to the first element if it is 20th from the end (that is, its parent has only 20 children).
Go to any of the following element could be something like this:
:nth-last-child(20):first-child ~ :nth-child(10) { }
This gave readability, but it still looked somewhat cumbersome.
It was just the time to recall that: first-child is a special case of: nth-child (1), in the end we got a selector
:nth-child(1):nth-last-child(20) { }
What made it possible to move right and left by any means, clearly “clinging” to the beginning and end of the list of children.
In real scss, it turned into a similar, somewhat monstrous design.
@for $i from 1 through 20 { @for $j from 1 through $i { .child:nth-child(#{$j}):nth-last-child(#{$i - $j}) { @include transform(rotate(360deg * (($j - 1) / ($i - 1)))) } } }
Here are some examples of how this works:
An example with different width of blocks depending on their numberAn example with a uniform distribution of elements in a circleThese examples are perhaps somewhat contrived, but in reality they show the real possibilities of such a “point” conditional reference to elements.
Of the minuses, I could name only the large size of the output css file: for a construction with 20 elements, each selector of which had 14 lines due to vendor prefixes, the length of the final style was about 2000 lines.
I do not know how else this can be used, but there is clearly something in it, something big and interesting. If you have ideas, I’ll be happy to add their examples here at the end of the post.