Skip to content

Instantly share code, notes, and snippets.

@xl1
Last active October 24, 2022 16:39
Show Gist options
  • Save xl1/8651716 to your computer and use it in GitHub Desktop.
Save xl1/8651716 to your computer and use it in GitHub Desktop.
CSS Regions で計算をする

CSS Regions で計算をする

検証環境

Google Chrome 34.0.1797.2 (Official Build 246002) dev-m
OS  Windows 
Blink 537.36 (@165430)
enable-experimental-web-platform-features フラグ オン

これまでの CSS での「計算」

CSS Programming という概念が生まれてから今年で 3 年。 CSS である程度の「計算」ができることは広く知られるようになりました[要出典]が、これまでは致命的な欠点を抱えていました。 それは、計算結果を再び入力として使うことができないという点です。

  • たとえば、counter-increment を使えばたし算・ひき算ができますが、その結果は content: counter(foo) として表示するだけで、新たな計算の入力としては使えません。
  • たとえば、CSS Calculator では、演算子は一度しか使えず、普通の電卓のように 1 + 2 + 3 + ... と連続して計算することはできません。
  • たとえば、HTML + CSS だけで一次元セルオートマトンを作ることができますが、計算結果をその次のステップで利用するために人間が Tab + Space を押してやる必要があります。

それ、CSS Regions でできるよ!

CSS Regions を利用した次のような HTML と CSS を考えます。

<input type="checkbox"><div id="input0" class="in"></div>
<input type="checkbox"><div id="input1" class="in"></div>
<div class="or"></div>
<div class="and"></div>
.in {
  flow-into: flow1;
}
input:checked + .in {
  height: 100px;
}
.or, .and {
  max-height: 100px;
  flow-from: flow1;
}

2 つの input があり、それらをチェックすると、その直後の .in の高さが変化して flow1 という名前の named flow に流れ込みます。 流れ込んだ .in は、後続の 2 つの div に流れ出すことになりますが、

  • input がチェックされていなければ .or.and も空。(empty)
  • input がどちらか 1 つだけチェックされていれば、.or には要素が流れ出している (fit) ものの、.and は空。
  • input が両方ともチェックされていれば、.or にも .and にも要素が流れ出す。

というわけで、AND と OR を計算するユニットができたことになります。 div を四角、named flow を丸で表すと次のような感じでしょうか。

orand

さて本題は、この結果を再び入力として利用することですが、簡単ですね。 .and { flow-into: flow2; } などとして、同じように flow-from: flow2 な要素も用意してやればいいのです。

::region() 疑似要素を使う

AND と OR だけでは不完全なので、NOT も作っておきましょう。 これには ::region() 疑似要素を使います(::region() なしで作る方法もあるかもしれませんが)。

<div class="not-plus" id="not-plus1"></div>
<div class="not-plus"></div>
<div class="not-plus"></div>
<div class="not-minus"></div>
<div class="not-minus" id="not-minus1"></div>
<div class="not"></div>
/* 前の結果が flow-into: flow2 されている前提 */
.not-plus, .not-minus {
  height: 100px;
}
.not-plus {
  flow-into: flow2;
}
.not-minus, .not {
  flow-from: flow2;
}
#not-minus1::region(#not-plus1) {
  margin-top: -200px;
}
  • 前の結果が流れ込んでこなければ、3 つの .not-plus.not-minus.not に流れ出す。
  • 流れ込んでくると、
    • まずその要素が 1 つ目の .not-minus に流れ出す。
    • 次の #not-plus1#not-minus1 に流れ出し、::region() のルールが適用される。200px 分の隙間ができる。
    • その隙間に残り 2 つの .not-plus が流れこむため、.not は空のままになる。

::region() のところを赤線で表すと次のように図にかけます。

not

残念ながら、::region() 疑似要素を使えるブラウザはまだありません。 Chrome (とたぶん Safari も)では古い @-webkit-region ルールが使えるのですが、margin-top はサポートされていません。 そのため、以下のデモでは JavaScript Polyfill を利用しています。::region() 疑似要素については「CSS Regions と ::region() 疑似要素」も参照してください。

以上で、AND, OR, NOT 等の基本的なユニットが完成し、また各ユニットの出力を入力に使うこともできるようになりました。 これで、(将来的にはきっと CSS だけで)論理演算ができます。めでたし、めでたし……

組み合わせて大きいモジュールに

flow の名前の処理が手書きでは煩雑なため、テンプレートエンジンなどを使って自動生成したいところです。 例えば、私は CoffeeCup で、regioncalc/regioncalc.coffee のようなテンプレートを用意していますが、まったく役に立たなくて便利かと思います。

できないこと

  • 出力を自分や、より上流に再入力して循環参照させることはできません。 できたとするといつまでもスタイルの計算が終わらなくなるため、CSS Regions の仕様で防ぐようになっています
  • polyfill が重い + setTimeout 等を乱用していて遅いため、スタイルが適用されるまでにタイムラグがあります。
    • そもそもこんなことをしているのも、無茶をすることで polyfill の不具合発見や性能向上に役立てたいという意図があります。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment