Skip to content
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] Function to retrieve index of element among siblings, or counter on element as integer #1869

Closed
Nokel81 opened this issue Oct 13, 2017 · 11 comments

Comments

@Nokel81
Copy link

Nokel81 commented Oct 13, 2017

Currently you can write the following with the :nth-child or similar style selectors

.foobar>*:nth-child(2n) {
    background-color: lime;
}

Which means that every second child has a background colour of lime. However you cannot do something like the following:

.foobar {
    display: grid;
    grid-template-columns: 100px 1fr;
}
.foobar>*:nth-child(2n) {
    grid-column: 1/2;
    grid-row: n;
}
.foobar>*:nth-child(2n+1) {
    grid-column: 2/3;
    grid-row: n;
}

I think that this would be very useful and would make working with grids quite a bit more easy

@tabatkins
Copy link
Member

What would happen in:

.foo > :nth-child(n) > :nth-child(2n) { grid-row: n; }

?

@Nokel81
Copy link
Author

Nokel81 commented Oct 13, 2017

The second one would overwrite the first value. A way around this would be to allow other letters besides n

@Loirooriol
Copy link
Contributor

I think allowing counter() to return an integer would be a better way to address this kind of problems.

.foobar {
  counter-reset: n;
}
.foobar > * {
  counter-increment: n;
  grid-row: counter(n integer);
}

@Nokel81
Copy link
Author

Nokel81 commented Oct 14, 2017

Having not known of counters before this, that is cool. Now, yes I believe that that is a much better way of addressing this problem

@tabatkins
Copy link
Member

An integer-returning counter() is definitely one way to address this. We could possibly also go with a more direct sibling-index() function that just returns your index in among your siblings, so you don't have to set up a counter explicitly. I can definitely see some uses for this.

If you want the index of an ancestor element to use in a further descendent, you just need to (a) register a custom property to have a <number> type, and (b) set the property using sibling-index() on that parent element. Then you can use var() on the descendant to fetch the value. (You need to register the prop to get CSS to evaluate the function at the place you originally used it; an unregistered var will just preserve the function itself and resolve it on the descendant instead.)

@tabatkins tabatkins added css-values-4 Current Work and removed css-variables-2 labels Oct 27, 2017
@tabatkins tabatkins changed the title [css-selectors] nth-child produces a local variable [css-values] Function to retrieve index of element among siblings, or counter on element Oct 27, 2017
@tabatkins tabatkins changed the title [css-values] Function to retrieve index of element among siblings, or counter on element [css-values] Function to retrieve index of element among siblings, or counter on element as integer Oct 27, 2017
@Loirooriol
Copy link
Contributor

If a specific sibling-index() function is added, I guess it could also accept an argument to be consistent with the of S syntax of :nth-child(). For example, sibling-index(S) would return the (1-based) index of the current element among its inclusive siblings that match the selector list S. The problem is if the element does not match S, then maybe return 0.

@Nokel81
Copy link
Author

Nokel81 commented Oct 28, 2017

The idea of a selector as well is cool but another way to get the parent's sibling count would be sibling-index(1p) for the first parent

@jakearchibald
Copy link
Contributor

jakearchibald commented Nov 3, 2019

This, along with a way to get the number of children in an element, would be really nice for staggered animations.

.parent {
  --parent-children-count: children-count();
}
.parent .anim {
  /* Stagger from start to end */
  animation-delay: calc(sibling-index() * 100ms);
  /* Stagger from end to start */
  animation-delay: calc((var(--parent-children-count) - sibling-index()) * 100ms);
}

(@Nokel81 maybe the above shows that you don't need a special way to get values from the parent)

@shshaw
Copy link

shshaw commented Dec 4, 2019

My entire Splitting.js library is based around giving these values as CSS variables. I would love for my library to become obsolete (though that would also require ::nth-letter as well 😄).

animation-delay and transition-delay are the most commonly used, but it also allows for other amazing effects. Here's a large collection of uses people have found for these values in CSS: https://codepen.io/collection/XpROaV/

My thoughts on the syntax would be:

  • child-count() returns the number of child elements
    (akin to ['A','B','C'].length)

  • sibling-index() returns the element's index in relation to its siblings
    (akin to ['A','B','C'].indexOf('B'))

  • sibling-count() returns the total count of adjacent elements
    (the parent's child-count() - 1 )

Using sibling-count() you can then calculate a 0-1 range of the element's "position" in the parent. For example:

.children {
  animation: fade-in 1s linear;
  /* Stagger in all children over the course of 1s */
  animation-delay: calc( 1s * ( sibling-index() / sibling-count() ) );
}

Demo using CSS variables

You can also calculate "distance" from the end (as @jakearchibald showed), or even calculate from the center (example using CSS Variables]

@jakearchibald
Copy link
Contributor

It seems like the newer issue, #4559, has more traction. Maybe this should close?

@tabatkins
Copy link
Member

Yeah, since #4559 is the version that actually got a WG resolution, I'll close this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants