Feb 18, 2016

スクロールバーの見た目を保ったまま細くする

ツリー型タブではタブバーに表示するスクロールバーについて、普通のスクロールバーよりも細く表示するようにしてるんだけど、その実装方法に少し改善があったので誰得だけど解説しておく。

ちょっと前までのバージョンでは、単純にCSSのmax-widthで以下のように実現していた。

tabs.tabbrowser-tabs
  .tabbrowser-arrowscrollbox
  > scrollbox
  > scrollbar[orient="vertical"],
tabs.tabbrowser-tabs
  .tabbrowser-arrowscrollbox
  > scrollbox
  > scrollbar[orient="vertical"] * {
  max-width: 10px;
  min-width: 10px; /* この指定がないと逆に最小サイズが大きくなってしまう */
}

tabs.tabbrowser-tabs
  .tabbrowser-arrowscrollbox
  > scrollbox
  > scrollbar[orient="vertical"] {
  font-size: 10px;
}

この方法の難点は、一部のプラットフォームで……というかWindowsでスクロールバーの端のボタンの表示がずれるということ。 (スクリーンショット) スクリーンショットを見ると、「▼」がちょっと右にずれてるのが分かる。 何故こうなるかというと、Windowsのスクロールバーの端のボタンのアイコンは最小サイズが大きめに定義されているようで、それよりも小さなサイズを指定してもWindows側の最小サイズが優先されてしまうせい。

この問題は、実用上は問題がないので長らく放置してたんだけど、最近ツリー型タブのissue trackerで閉じられてないままの古いissueを断捨離してる時にもっとこうしたらいいよという提案があったことに今頃気がついて、その方針を最近のバージョンで採用してみた。 (スクリーンショット)

これはどうやってるのかというと、スクロールバーの幅を小さくする代わりに、「スクロールバーの左右にマイナスのマージンを設定して他の要素の下に潜り込ませる」ことで、擬似的に細く見せている。 何ピクセル潜り込ませるかは環境によって変わってくるので、document.getComputedStyle()なども使ってその都度計算するという面倒なことをしている。 これで、「▼」がずれるということがなくなった。

しかし、今度は副作用としてタブバーの背景色を変えている時に表示が変になるという問題が起こってしまった。 例えばClassic Theme Restorerをインストールして且つ「Mixed」スキンを選択した状態だと、水色の背景の一部がグレーになってしまってた。 (スクリーンショット) これは、先の「マイナスのマージンを設定して他の要素の下に潜り込ませる」という手法において、スクロールバーが潜り込む先となる(スクロールバーの上に載って一部を覆い隠す)ボックスの背景色の指定が必要となってしまって、決め打ちで指定した色がユーザのカスタマイズ後の色と違うせいで見えてしまっているということ。 タブバーの背景色を変える方法はuserChrome.cssやらテーマやらClassic Theme Restorerやら色々とあるので、すべての場合にマッチする万能の対策は事実上無い。

ということで頭抱えてしまって、なんとかスクロールバーの潜り込んだ部分(はみ出た部分)を綺麗に消す方法はないかと、思いついた方法を色々試してみた。

  • スクロールバー全体にネガティブマージンを指定するのがアウトなら、ボタン部分だけネガティブマージンを指定するのはどうか?(元々、ボタン部分以外はmax-widthでちゃんと細くできてたんだし)と思って試してみたら、ボタン部分だけがスクロールバーからはみ出すという結果になってしまった。 (スクリーンショット)
  • 横方向にはみ出した部分を見えなくする、というとoverflow-x:hiddenだな!と思って、スクロールバー全体にこれを指定してはみ出したボタンだけをどうにかしようと思ったけど、実際やってみたらスクロールバー全体の動作がぶっ壊れてしまうので駄目だった。 (スクリーンショット)

万策尽きてDOMインスペクタの画面とにらめっこしてた所、clipという名前が見えた。そういえばCSSにはそういう機能もあるんだった。試したこと無かったから、じゃあそれで行けるんじゃないか? ということで実際試してみたんだけど、これもスクロールバーのボタンには効いてくれなかった。

でも、MDNのプロパティ解説を見るとdeprecatedと書いてあって、誘導先を見たらclip-pathを使えと書いてある。 clip-pathというとFirefoxのタブの見た目などを丸くするのに実際使われてるプロパティだったはずなので、こっちなら大丈夫だろうと思って再挑戦したら、ようやくうまくいった。 (スクリーンショット) Classic Theme Restorerとの併用時にも何ら問題がないことが見て取れる。

実装は以下のようになってる。

tabs.tabbrowser-tabs
  .tabbrowser-arrowscrollbox
  > scrollbox
  > scrollbar[orient="vertical"] {
  font-size: 10px;
  max-width: 10px;
  min-width: 10px;
  clip-path: url(#treestyletab-box-clip-path);
}
tabs.tabbrowser-tabs
  .tabbrowser-arrowscrollbox
  > scrollbox
  > scrollbar[orient="vertical"] * {
  font-size: 10px;
  max-width: 100%;
  min-width: 10px;
}
tabs.tabbrowser-tabs
  .tabbrowser-arrowscrollbox
  > scrollbox
  > scrollbar[orient="vertical"] scrollbarbutton {
  font-size: 10px;
  margin-left: -3px;
  margin-right: -2px;
}

参照してるSVGのクリッピングパスは以下の通り。

<svg:svg height="0">
  <svg:clipPath id="treestyletab-box-clip-path"
    clipPathUnits="objectBoundingBox">
    <svg:path d="m0,0 V1 H1 V-1 H-1 z"/>
  </svg:clipPath>
</svg:svg>

このクリッピングパスはどういう形かというと、仮想的なキャンバス全体を覆い尽くす矩形になってる。mから始まる相対指定で(0,0)から(1,1)までを覆う矩形を作っている(左上から始めて、下、右、上、左と反時計回りに進んでパスを閉じてる。最初は逆方向の時計回りに回ってしまってうまくいかなかった)ので、クリッピングパスの適用対象になるボックスがどんなサイズであってもそれと同じ大きさになる。 スクロールバーに対してこのクリッピングパスを反映すると、この矩形からはみ出る部分、つまりネガティブマージンではみ出してるボタンが切り取られて表示されるようになる、というわけ。

今回の実装自体のノウハウをそのまま流用できる場面はそうそうなさそうだけど、なんかの機会にまた使うかもしれないので、解説してみた。 要点をまとめると、「要素のスクロールの可否といった動作に影響を与えないで、見た目の表示サイズだけを小さくしたい時は、clip-pathを使うとよい」といったところでしょうか。

以上、ちっこい三角ひとつのためだけに四苦八苦させられた話なのでした。

エントリを編集します。

wikieditish message: Ready to edit this entry.











拡張機能