CSS の nth-child と variable で、要素の順番に応じた変化をつける方法

##100個の要素の順番を、21個のセレクタで特定して変化をつけたcodepenのデモ
<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="css,result" data-user="ksksoft" data-slug-hash="PooqbMZ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Use nth-child as CSS variable">
  <span>See the Pen <a href="https://codepen.io/ksksoft/pen/PooqbMZ">
  Use nth-child as CSS variable</a> by keisuke Takahashi (<a href="https://codepen.io/ksksoft">@ksksoft</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
- - -
##要素の順番を取得するならcounterでは？
要素の順番はCSSの` counter` で取得できます。カスタマイズしてリストマーカー的に使ったりするアレです。
しかし、` counter` で取得した内容はテキストなので、擬似要素に出力はできても、variableで` calc()` できません。つまり、計算には使えない残念さんです。
- - -
##要素数が少なければ、順番毎に処理を書いてしまえ
3つの要素に順番に変化をつけたい場合、nth-child()を3つ書けばいいです。

```:html
 <ul>
  <li>one</li>
  <li>two</li>
  <li>three</li>
 </ul>
```

```:css
li {
  background-color: pink;
  list-style: none;
  width: 10em;
  animation: roll infinite 10s 0s both;
}

/* 開始から3秒は停止、2秒使って1回転、その後停止させるkeyframes */
@keyframes roll {
  0%,
  30% {
    transform: rotate(0turn);
  }
  50%,
  100% {
    transform: rotate(1turn);
  }
}

/* 同じkeyframesを使い、要素の順番に応じてanimation-delayだけを変える */
li:nht-child(1) { animation-delay: 0s;}
li:nht-child(2) { animation-delay: 1s;}
li:nht-child(3) { animation-delay: 2s;}
```

- - -
###keyframesは使い回す
同じ動きなら同じ` keyframes` を使う、というのがCSS` animation` のコツです。` keyframes` の%を要素の順番で計算してずらす、__なんてことはやらないように__。
まず` keyframes` を要素の数だけ書くのは大変ですし、` animation` の動きを変えたくなった時に、全ての` keyframes` の%を調整するハメになります。
- - -
##でも、要素数が100個ならどうする？
` :nth-child(1){}` から` :nth-child(100){}` まで書けばできます。
できますが、` :nth-child(An + B){}` のようにn変数を使って記述したいところです。
- - -
###要素の順番を特定する機能を切り離す
codepenで1から100までを特定した部分を抜粋します（__ここがポイントです__）。

```:css
  /* Get element order */
  li:nth-child(10n + 0) { --digit-1: 0; }
  li:nth-child(10n + 1) { --digit-1: 1; }
  li:nth-child(10n + 2) { --digit-1: 2; }
  li:nth-child(10n + 3) { --digit-1: 3; }
  li:nth-child(10n + 4) { --digit-1: 4; }
  li:nth-child(10n + 5) { --digit-1: 5; }
  li:nth-child(10n + 6) { --digit-1: 6; }
  li:nth-child(10n + 7) { --digit-1: 7; }
  li:nth-child(10n + 8) { --digit-1: 8; }
  li:nth-child(10n + 9) { --digit-1: 9; }

  li:nth-child(-n + 10) { --digit-2: 0; }
  li:nth-child(n + 10):nth-child(-n + 20) { --digit-2: 10; }
  li:nth-child(n + 20):nth-child(-n + 30) { --digit-2: 20; }
  li:nth-child(n + 30):nth-child(-n + 40) { --digit-2: 30; }
  li:nth-child(n + 40):nth-child(-n + 50) { --digit-2: 40; }
  li:nth-child(n + 50):nth-child(-n + 60) { --digit-2: 50; }
  li:nth-child(n + 60):nth-child(-n + 70) { --digit-2: 60; }
  li:nth-child(n + 70):nth-child(-n + 80) { --digit-2: 70; }
  li:nth-child(n + 80):nth-child(-n + 90) { --digit-2: 80; }
  li:nth-child(n + 90):nth-child(-n + 100) { --digit-2: 90; }
  li:nth-child(n + 100):nth-child(-n + 110) { --digit-2: 100; }
```

途中の改行を挟んで、前半と後半に分けて説明します。

- - -
###【前半】要素の順番の1の位を特定する
前半の10個のセレクタで、要素の順番の1の位を特定します。
` :nth-child(An){}`は、「A個ごとに〜」という意味になります。順番は、10進数で取得したいので、Aに10を入れます。
` :nth-child(10n + B){}`  とすれば、「10個ごとにBを足す」というセレクタになります。
例えば、` :nth-child(10n + 1){}`  にマッチする要素の順番は、

|n|代入して計算|結果|
|:---:|:---:|:---:|
|0|(10 * 0 + 1)|1|
|1|(10 * 1 + 1)|11|
|2|(10 * 2 + 1)|21|
|3|(10 * 3 + 1)|31|
|---|---|---|
|9|(10 * 9 + 1)|91|

となり、要素の順番の1の位が全て1になる、という仕組みです。
こうして取得した1の位を、`--digit-1`と定義したvariableの値として代入できます。

- - -
###【後半】要素の順番の10の位を特定する
後半で、要素の順番の10の位を特定します。ここで使用する`nth-child`は2種類あります。

|セレクタ|意味|
|---|---|
|:nth-child(-n + C)|C以下にマッチ|
|:nth-child(n + D):nth-child(-n + C)|C〜Dの範囲にマッチ|

したがって、`:nth-child(-n + 10){}`は、10以下の要素にマッチします。
1から10番目の要素では、10の位として`--digit-2`と定義したvariableが、0になります。

次の `:nth-child(n + 10):nth-child(-n + 20) {}` は、10〜20の要素にマッチして、`--digit-2`に10の位として 10 が代入されます。
ちなみに、10番目の要素が、直前のセレクタと重複してますが、`:nth-child`が2回使われているので、その分、セレクタの詳細度が上がり、こちらが優先されます。

以下、20〜29には20が、30〜39には30が代入されていき、10の位が99まで特定されます。各セレクタの数字を10刻みにしているのは、可読性をあげて理解しやすくするためです。

最後のセレクタは100番目の要素になり、10の位は100になります。
ここを、３桁目にして、`--digit-3`とするのが正しいでしょうが、今回は1から100までを特定するということで省略しました。

- - -
###var()に要素の順番から計算した変数を使えば、記述は1回で済む
再び、codepenから抜粋します。

```:css
li {
  /* ここまでは省略。以下が肝要。 */
  --digit-1: 0;
  --digit-2: 0;
  --h: calc(var(--digit-1) + var(--digit-2));
  --delay: calc( (var(--digit-1) + var(--digit-2)) * 0.1s);
  background-color: hsla(var(--h), 100%, 60%, 0.9);
  animation: roll infinite 10s var(--delay) both;
}
```

念の為、`--digit-1`と`--digit-2`に0を代入しています。
`--h`が要素の「順番」です。`--digit-1`と`--digit-2`を`calc()`で足して`--h`に代入しています。
`--delay`は その順番に0.1秒を掛けた値です。もちろん、直前に算出した`--h`に0.1秒を掛けても良いです。

- - -
###要素順に応じて変化させる
codepenのデモでは、背景色とアニメーションの開始するタイミングを変化させてみました。
`background-color`の`hsla()` の `h`の部分に、`var(--h)`を使い、色相を変化させます。
`animation`の指定では、1つ目の秒数が`animation-duration`になり、2つ目の秒数が `animation-delay`になる決まりなので、2つ目に`var(--delay)`を指定します。

__これで、要素数が未定でも、変化がつけ易くなります。__


- - -
##記述量は減らせたか？
要素の順番毎にセレクタと処理を書いて、変化を実装する場合、要素が多くなると、数に比例して記述量が増えてしまいます。
###順番毎に処理を書く場合

|要素数|セレクタの数|varの数|
|:---:|:---:|:---:|
|10|10|---|
|100|100|---|
|1000|1000|---|

###順番を特定する部分を分離した場合

|要素数|セレクタの数|varの数|
|:---:|:---:|:---:|
|10|11|1|
|100|21|2|
|1000|41|3|

特定したい要素数が増えるほど、恩恵にあずかれる、という主張です。

###処理は一回だけ書けば良い
特定した順番を変数として利用すれば、一回だけ処理を書けば良いことになります。要素の順番毎にセレクタと処理を書く必要はありません。記述もスッキリします。

- - -
##おわりに
世界でなら500人、日本でも30人くらいに、この投稿やcodepenが刺さると良いなあ、と思います。
Qiitaへの投稿は初です。無作法があったらすいません。
###CSSメタ言語を使えばイイじゃん、と思ったあなたへ
LessとかSassとかでmixinとかforとかするのかもしれませんが、私はメタ言語は使わないので、パスします。
1000個のセレクタを簡単に書けそうですが、コンパイル後の行数は増えちゃう気がしますが、どうなんでしょう？
CSSメタ言語での記述がお分かりになる方は、コメントやご自身の投稿で、その方法を補足してください。

###javascriptを使えばイイじゃん、と思ったあなたへ
CSSだけでやるのが面白いし、好きなんだよ、という主張なんです。