New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[css-values] Proposal: add sibling-count() and sibling-index() #4559
Comments
|
Definitely on board for this. These functions would pave the way for making Splitting.js obsolete and enable some really great animations and effects. Small point for discussion: Should |
|
Proposal looks good, although it'd be nice if an element could know it's children count too. |
|
I'm not sure why we have two issues though. Feels like one of them should be closed. |
|
Maybe there should also be a depth query (with some consideration for what happens in shadow trees). |
|
This looks like a straight duplicate of #1869, can we close this & copy the discussion over there? That conversation discusses the |
|
Tab and I felt a new one was due since there were a few issues that alluded to similar functionality but generally had |
|
Why a function? (With an implementor hat on) it's a bit unfortunate having to do the element instead of node count, as the first is |
|
What could we use that's not a function @emilio!? Element vs Node, browser internals don't do any distinguishing that you can piggyback on? 0(N), even if the children are fixed and there's no recursion required? tell me more, this sounds interesting 🙂 |
|
Well, I was thinking there's nothing really preventing you from using an ident or such (functions without arguments are not great), but I guess that would make some properties that can take
At least Gecko keeps the node children count in the parent node (here), so getting the sibling node count is just There's nothing preventing us from tracking element count either, but it grows every node (or at least container node) which is not great. But it seems other engines don't do the same optimization as Gecko, so feel free to take that point as an implementation detail... |
|
This feels like it should be solved either with predefined counters or with predefined variables that do not have a double-hyphen prefix.
|
|
I'd been trying to work up a coherent way to do this using I would personally lean towards a new function over a syntax that was a) not a function or b) used Can I suggest that instead of ol[reversed] {
counter-reset: list-item count(children);
}It would be good to leave that option available in the syntax, even if it's not implemented yet. (edit: see also #4181) Idle musing on other uses: .clockhand {
position: absolute;
left: calc(50% + sin(index(sibling) / count(sibling)) * var(--radius));
top: calc(50% + cos(index(sibling) / count(sibling)) * var(--radius));
} |
|
In #1869 (comment) I showed that if you can get a children count, you can use that to get a sibling count, so if we need an minimum viable product, then I'd rather have a children count than sibling count. I also think "sibling count" is misleading. For example, I am one of two children, but doesn't that mean I have one sibling? Whereas here we're using "sibling count" to mean "sibling count + myself". |
|
How about |
|
I don't mind too much between CSS doesn't really allow you to interact with non-element nodes so adding something like |
|
I like the flexibility of the `index()` and `count()` concept, especially
if a selector could be passed in.
A few possibilities that would be great to see:
* `count(children)` for the number of child elements (akin to `count(“>*”)`)
* `count(“.media”)` for counting the children matching a selector
* `count(siblings)` for getting parent’s element count minus 1.
* `index(siblings)` for getting the element’s index relative to its siblings
* `index(n)` for getting the n value in :nth-child loops for more advanced selecting
Imagine using `count()` to help you determine the number of `grid-columns`!
I realize not all of those would be feasible or possible initially, but having open ended functions could allow their expansion in the future.
|
Does anyone have a use-case where "parent's element count minus 1" is the number you actually want, rather than "parent's element count"? |
|
@jakearchibald https://codepen.io/shshaw/pen/LYEEKMQ This can of course be achieved by doing |
This assumes that the index is 0-based. I would prefer it to be 1-based for consistency with nth-child. |
|
Consistency with nth-child would be good, and avoiding divide-by-0 errors is certainly ideal. Consider me on board for a 1 based `index()` and `count(siblings)` including the element itself.
|
|
I can immediately see the value of both
For the second idea I've made a plugin to help in the past, though now when I want to use this concept I usually reach for CSS custom properties and a little JS for a cleaner approach. Having the awareness of a tag's index inside |
|
So I agree with the proposed set of four functions:
I get the concern on precision, but I don't know of another word covering the concept, and "sibling" can be used in this sense (like "I'm one of three siblings"). I think it's the most reasonable name. I do not like being less specific with something like
Node count is just so, so un-useful. ^_^ But also, having these functions disagree with the :nth-child() selectors would be a terrible idea imo.
Hm, yeah, could go either way. I don't plan on extending the functions to more stuff, so I guess keywords could work. I'm slightly wary of adding "keywords that can be used anywhere" because of the potential syntax conflicts (such as animation-name in the 'animation' property); functions avoid that. (We should have ensured that author-defined names were syntactically distinguishable in all cases earlier, but that's a legacy mistake.) If these were usable only in calculations (that is, you have to wrap it in a calc() or other math function), my concern would be alleviated. Tho the suggestion to later extend this to allow a selector, a la the
Definitely 1-indexed, just like :nth-child(). Diverging from :nth-child() would be a terrible mistake.
I think the concept is a lot cleaner to express and understand if it's just "how many children there are"; both "sibling count" and "child count" should agree. Some cases definitely want "siblings other than me", but that's trivial to do in a calc(), and you're probably already using a calc() anyway. (The given example is, to do a division as well.)
Not planning on doing this; it would be super expensive. Use-cases are small enough, as far as I'm aware, that I'm happy to leave that to JS for decorating the element with a value.
This would be a misuse of the var() syntax, and have unfortunate implications regarding parsing; we wouldn't be able to reject a |
|
I always expected that Anyway, selectors in property values always seem like a bad idea, so Iʼm against a proactive function syntax without parameters, which probably appears natural only to a programmerʼs brain. In pseudo-classes, there are already precedents of something like |
|
Current state of proposed
Both are possible using /* fig.1: skip hidden <tabs><tab>(1)</tab><tab hidden>(skipped)</tab><tab>(2)</tab></tabs> */
tabs { counter-reset: index; }
tabs > tab:not([hidden])::before { counter-increment: index; content: 'Index: ' counter(index); }
/* fig.2: nesting depth <p><em>(1)<em>(2)<em>(3)</em></em><em>(2)</em></em></p> */
p { counter-reset: depth; }
p em::before { counter-increment: depth; content: 'Depth: ' counter(depth); }
p em::after { counter-increment: depth -1; content: ''; }I think above use cases are quite relevant for problem in question. Unless current proposal will be expanded so that
(yes, using selectors in properties feels super weird), or any other way, I'd rather lean towards #1026. Please pardon unasked intervention and errors; I don't follow all conversations around here, so this had very likely been discussed before; I just felt it should be mentioned here. |
|
@myfonj While counters can be more flexible, they have some complex inheritance. In order to know the value of a counter in an element, you may have to iterate all the descendants of the previous siblings of the element. And this can be bad for parallelization, see #1026 (comment). So it seems less doable to me. |
|
Would |
|
@jonathantneal That would create a circularity, because you should be able to use |
|
Yeah, the functions would definitely just be counting based on DOM, exactly the same as :nth-child(). |
|
I appreciate and agree with the responses to my last question. Thank you! Might these related functions be folded into one? Borrowing from an earlier example: /* stagger the animation, start to end */
.animate > g {
animation-delay: calc(this(sibling-index) * 100ms);
}
/* stagger the animation, end to start */
.animate > g {
animation-delay: calc(this(sibling-count) - this(sibling-index) * 100ms);
}I experienced slowness reading Anyway, this is my own experience I’m sharing in case other people feel the same clarity after seeing the above example. |
What if I don't want to skip the element but particularly want to target the children? As if you have a /* Element with display: contents */
.contents {
display: contents;
--index: sibling-index();
}
/* Child element of a wrapper with display: contents */
.contents-child {
--sibling-index: calc(var(--index) + sibling-index());
}That way you would pass down the value of the |
Yeah, I don't have any good suggestions. parent-children-count is 'technically correct', but sounds weird. |
|
@Que-tin That should work as long as you register |
|
Can't quite grasp what the later means, but registering as shouldn't be the problem here. |
|
How will |
|
@kbrilla See #4559 (comment) and #4559 (comment) |
thx, I was looking for |
|
@kbrilla yep, for the same reason |
|
I still think it's pretty weird that we're using "sibling count" to mean "number of children the parent has". |
Agree, isn't sibling-count wrong anyway from a language standpoint? Don't we mean smth. like sibling-count including myself / total-sibling-count? |
|
However, I think it's more useful to include the current element when counting, and |
|
@Loirooriol And what about |
|
BTW, IIUC current proposal suggests that And also, is the consensus about mechanism for "skipping" items that it will leverage the Is it correct? [1] "Index" being term that majority [citation needed] of modern programming languages use for zero-based notations? I have nothing against it, just liked how CSS avoided [2] the list-item-indices-start-at-one ("FORTRAN") camp with that clever naming of |
That's fair. I missed that comment. |
|
We already bikeshedded the names. And for indexes, CSS always 1-indexes, and specifically |
|
I'm excited for this to land, I'm starting to see more examples of ol {
&::after {
display: block;
margin-block-start: 1rem;
font-size: 2rem;
}
&:has(> li:last-child:nth-child(1))::after {
content: 'sibling-count() = 1';
}
&:has(> li:last-child:nth-child(2))::after {
content: 'sibling-count() = 2';
}
&:has(> li:last-child:nth-child(3))::after {
content: 'sibling-count() = 3';
}
&:has(> li:last-child:nth-child(4))::after {
content: 'sibling-count() = 4';
}
&:has(> li:last-child:nth-child(5))::after {
content: 'sibling-count() = 5';
}
/* ... */
} |
Problem
Currently we can query a child based on it's child index position or query on children length (with some complex syntax), but we can't use the index or length as values in our styles. Feels like they're known, but not accessible.
so we end up doing stuff like this:
Proposal
2 new functions:
sibling-count()andsibling-index(). These should report values based on element count, not node count.sibling-count()This function returns the total length of sibling elements as a number, similar to
node.childElementCount(docs) ornode.children.lengthbut accessible from a sibling. Consider this like a child asking "how many siblings they have".example usage
sibling-index()This function returns the contextual child index as a number. Similar to the value queried with
nth-child, this would be a get() call for the contextual child's index in the tree. Consider this like a child asking "what position am I in this family".example usage
All Together
Use Cases
Conclusion
Most other proposals for similar functionality, request access to
counters()as a unit that can be passed tocalc(). But I feel that is overloading the counters feature and is rooted in a mindset of leveraging something "close" to what is needed, where what's actually wanted is the child index position for visually reasonable and meaningful UI feedback and presentation.By proposing a solution that doesn't involve counters, I hope to bypass much of the pain points associated with the feature to help unblock the large and ever-growing set of use cases that could leverage these contextual values.
Sources & Chatter
#1869
#1176
#1026
https://www.w3.org/TR/selectors-4/#child-index
https://twitter.com/shshaw/status/1201978228375724032?s=20
https://twitter.com/smfr/status/1202276694230306816?s=20 @smfr
The text was updated successfully, but these errors were encountered: