argyleink / open-props

CSS custom properties to help accelerate adaptive and consistent design.
https://open-props.style
MIT License
4.79k stars 193 forks source link

Add sibling-index and sibling-count as custom properties #506

Open myfonj opened 4 months ago

myfonj commented 4 months ago

A bit silly feature idea: introduce optional import(s) (or JS generator in the "bookmarklet" include) emulating upcoming sibling-count() and sibling-index() proposal (by some @argyleink dude) in a brute-force ad-nauseam low-key manner:

--sibling-index

*:nth-child(1) { --sibling-index: 0; }
*:nth-child(2) { --sibling-index: 1; }
/* ... */
*:nth-child(<N>) { --sibling-index: <N-1>; }

(*), and similarly

--sibling-count

*:has(> *:last-child:nth-child(<N>)) > * { --sibling-count: <N>; }

or for super backwards ":has()-less" compatibility (or perhaps better performance(?)) the way Lea showcased in the distant past:

*:first-child:nth-last-child(<N>),
*:first-child:nth-last-child(<N>) ~ * { --sibling-count: <N>; }

Open question remains what initial threshold to pick (1-99?) and whether pre-generate further chunks (100-999, 1000-9999, ...?) in separate includes.

Most probably ideal alternative to includes in client-side environments with scripting available could be a dynamic JS polyfill with lazy mutation observer spitting out additional chunks into in-page <style> when maximum sibling count increases somewhere in the tree..


(*: I am consciously proposing zero-based indexing here as god intended, even though I clearly know it has zero-chance to be adopted this way; I just had to try 🙃. For 1-based property I'd propose --sibling-order. )

argyleink commented 4 months ago

I see what you mean here! Nice little microjs file to aid in adopting the --sibling-index idea 🙂

I could def see this as a classname/attribute and a js import which has a mutation observer too?


I am consciously proposing zero-based indexing here as god intended

lol, I mean, if it's your function then you get to make the choices! lots of good ideas come from building tools that match your preferences.

kizu commented 1 month ago

I mentioned this issue in my latest article, so wanted to link back: https://kizu.dev/tree-counting-and-random/

From all my experiments there, I think it could be feasible to have reusable --sibling-index and --sibling-count up to ~100 elements. The --sibling-index requires only 19 rules to cover 99 elements, but the --sibling-count will need more if it will be using the ~ method. It is possible to make it more compact with :has(), but it is also much more performance-intensive — my article initially frozen Safari due to it.

The ~ method, though, seems pretty well optimized in all browsers, and has an excellent browser support. So if you don't need to write it from scratch and if it covers ~100 elements, that would be enough for most cases.

argyleink commented 1 month ago

given your work here @kizu was put into a prop pack (minor changes made to generalize it) here in Open Props:

.provide-sibling-indexes {
  & > * {
    --si2: 0;
    --si1: 0;
    --sibling-index: calc(10 * var(--si2) + var(--si1));
  }
  & > *:nth-child(10n+1) { --si1: 1 }
  & > *:nth-child(10n+2) { --si1: 2 }
  & > *:nth-child(10n+3) { --si1: 3 }
  & > *:nth-child(10n+4) { --si1: 4 }
  & > *:nth-child(10n+5) { --si1: 5 }
  & > *:nth-child(10n+6) { --si1: 6 }
  & > *:nth-child(10n+7) { --si1: 7 }
  & > *:nth-child(10n+8) { --si1: 8 }
  & > *:nth-child(10n+9) { --si1: 9 }
  & > *:nth-child(n+10):nth-child(-n+19) { --si2: 1 }
  & > *:nth-child(n+20):nth-child(-n+29) { --si2: 2 }
  & > *:nth-child(n+30):nth-child(-n+39) { --si2: 3 }
  & > *:nth-child(n+40):nth-child(-n+49) { --si2: 4 }
  & > *:nth-child(n+50):nth-child(-n+59) { --si2: 5 }
  & > *:nth-child(n+60):nth-child(-n+69) { --si2: 6 }
  & > *:nth-child(n+70):nth-child(-n+79) { --si2: 7 }
  & > *:nth-child(n+80):nth-child(-n+89) { --si2: 8 }
  & > *:nth-child(n+90):nth-child(-n+99) { --si2: 9 }
}

which then authors could import like:

@import "open-props/sibling-indexes";

and apply to an element like:

<ul class="provide-sibling-indexes">
  <li>…</li>
  …
</ul>

then use like:

ul {
  li {
    transition-delay: calc(var(--sibling-index) * 50ms);
  }
}

that the kind of flow you're imagining too?

kizu commented 1 month ago

Yep! I did not generalize because I was already feeling the performance hits, but these were mostly from those selectors with :has(), in this case it should not matter if my guesses about why there are performance hits are correct.