カラーモード

CSSだけでviewportに常に対角線を引く

CSS

画面に対角線をどうしても引きたい時が人生に一度や二度は来ると思う。

任意の角度で斜め線を引くなら linear-gradient()<color-stop> を工夫したり、線だけの要素を transform: rotate() するなど手法はいくつかある。しかし viewport の対角線においては閲覧環境によってその角度が異なるので、角度を動的に算出する必要がある。

一見望みがありそうな calc() はどうだろうか。平方根や三角関数を直接求めることは 2016 年 4 月時点ではできないが、CSS Variables を利用し、計算結果を保存して別の calc() に渡していけばできるかもしれない。

長方形と対角線の長さと角度の関係

viewport を長方形と見ると、幅 100vw で高さ 100vh となる。これで斜辺 C の長さがわかれば、対角線の上側と下側の角度がラジアン単位で求められるので、linear-gradient() の第一引数や rotate() に指定すれば解決だ。

なのだが、前述した通り平方根を直接求めることはできない。平方根を逐次計算で求めていく方法もあるようだが、100vw の二乗、100vh の二乗とはいったいいくつなのだろうか?  Computed Value では実数になっているけど、それは Computed Value での話だし、CSS Var で --num: calc(100vw * 100vw); としても、乗算では引数は少なくとも1つが <number> でなければならないのでエラーになる。

ということで、どう頑張ろうとも CSS だけで動的に角度を求めることはできない。

でも CSS でできるよ

できないのは「動的に角度を求めること」なので、違うアプローチで対角線を表現する。

具体的には、border プロパティで三角形を作る方法を応用し、直角三角形を重ねて対角線を表現する。

CodePen にデモを作った。

Windows ユーザーの人は toggle menu ボタンを押すとスクロールバーが出てアレするが、それについては後述しているので一旦置いておいてほしい。

やってることを言葉で詳しく説明すると下記の通りだ。

右上から左下への対角線の場合は border-color で色を指定する箇所を変えればよい。

重なりのイメージ

内容物の幅と高さが共に 0 で、要素の大きさが border-width 分しかない場合、その border-width 分の大きさの要素が作られ、隣接する border の間には色の境界線ができる。そして対角に位置する方の「border 同士の境界線」と直線で結ばれる。境界線の角度は要素の大きさで決まり、要素の大きさは border-width の合計値で 100vw100vh になっているので、viewport に対角線が引かれているのと同義になる。ちょっとわかりやすくなるかもしれないサンプルも作った。ウィンドウサイズをぐいんぐいんすれば対角線の維持される様子がわかると思う。

デモでは画面に対して position: fixed で固定配置しているので、スタック文脈の解決のためにコンテンツエリアも position: relative する必要があるので注意。

ウィンドウサイズぐいんぐいんおじさんがデスクトップ版 Safari を使っている場合、「ウィンドウサイズをぐいんぐいんすると対角線が追随しないぞ」というバグ報告を絶対にしてくる。なので強制再描画ハックもつけた。これでウィンドウサイズぐいんぐいんおじさんも満足するはずだ。Firefox や Chrome では強制再描画しなくてもウィンドウサイズぐいんぐいんに対角線が追随するので偉い。

問題点

まずブラウザの CSS 対応的に Viewport Unit が必要というのが絶対条件になる。border-width には <percentage>型 は許容されておらずborder-width: 50% のような指定は使えないので Viewport Unit に対応していないブラウザでは諦めざるを得ない。

また、border-color が単色しか指定できないので、コンテンツの背景も単色でなければならない。背景画像があるとかグラデーションを入れているとかだと対応できない。

そして一番の問題点は「スクロールバーで隠れる」ことと「Mobile Safari 9 の UI で隠れる」ことだ。

問題の前者は主に Windows 環境下での話。ページにスクロールバーが現れていると「見た目の画面幅」と 100vw の結果が異なるというやつ。Windows でデモの toggle more のボタンを押せばコンテンツが増えてスクロールバーが出るので、どういうことかわかると思う。

Edgeでスクロールバーが表示されていない場合

Edgeでスクロールバーが表示されている場合

問題の後者は iOS 環境下で、Mobile Safari 9 の UI(戻るボタンとかインテントボタンとかのやつ)が viewport にかぶさって「見た目の画面高」と 100vh の結果が異なるというやつ。ロケーションバーの方ではなく画面の下側に出る方の UI が問題になる。ページをスクロールするとその UI は隠されて見た目と結果が揃うのでやっかい。Mobile Safari 9 に依存した問題なのでモバイル版の Google Chrome で閲覧すればこの問題は起きないが、それでいいのかと手斧が飛んできたら防げずに死ぬ。

Mobile SafariでUIが表示されていない場合 Mobile SafariでUIが表示されている場合

ブラウザ UI を除いた表示領域の大きさを CSS だけでは動的に得られない。特に今回は対角線の描画に border を使っているので、% を指定できない点が痛い。

「CSS でできる」と言いながらも、できているのは OS X でだけである。それ以上でも以下でもなく、他のことを一切解決できないまま結局死ぬ。


CSS お絵描きの未来

Pure CSS でアートワーク的なことはこれまで様々なものが紹介されてきた。しかし本気でやろうとしたら JavaScript の補助は必要になってくる。今回のように閲覧環境やコンテンツの状況によって変わる値を必要とする場合はなおさらだ。

CSS の表現の幅の拡張で、その界隈では CSS Houdini に一瞬注目が集まった。でも結局中では JavaScript で実装を書くんだから、JS からは逃れられないのは変わらない。カスタムファンクションとして CSS だけで動いているように見えるだけだ。

CSS お絵描きに未来はあるか。あるとすれば高等数学をサポートした calc() が登場するか、ブラウザ UI を考慮した表示領域を扱える単位かプロパティーが登場する時だろう。しかし CSS Houdini が策定されていくとなれば、そちらでやれということにしかならなそうな気もする。未来なさそう。