preの背景をブラウザの幅いっぱいに拡大する

ここ最近のコードブロックのスタイリングの流行りに、本文よりもコードブロックが左右に飛び出すようなデザインがある。有名所だとQuirksModeで見ることができる(ここが発祥かもしれない)。このデザインはmarginプロパティで負の値を指定し、paddingプロパティでその分戻すことによって実現している。そのため非幅固定か幅固定でもQuirksModeのように左寄せならこの方法で実現できるが、うちのサイトのように幅固定で中央寄せな場合は左右のマージンが不定なため実現できない。しかし:before及び:after擬似要素を使うまったく別の方法でも実現することができた。

View Demo: Expand pre Background

ブラウザをリサイズしても幅固定と中央寄せのレイアウトを崩すことなくpre要素の背景がブラウザの幅いっぱいに拡大しているように見えるのが確認できると思う。

pre {
  position: relative;
}

pre:before,
pre:after {
  display: block;
  position: absolute;
  top: 0;
  width: 10000px;
  height: 100%;
  content: "";
}

まずpre要素にposition: relative;と指定し、その上で:before及び:after擬似要素をposition: absolute;と指定する。こうすることによって2つの擬似要素をpre要素の位置を基準とした絶対配置ができるようになる上に、widthプロパティとheightプロパティの長さの基準がpre要素の幅と高さになる。つまり、pre要素と同じ高さの空のブロックを作り出すことが可能になる。幅はブラウザの幅が極端に広いケースを考慮して、一応10000pxなどと大きめな値を指定しておく。

pre:before {
  right: 100%;
}

pre:after {
  left: 100%;
}

あとはこの2つの擬似要素を左右にぴったりとくっつくようにrightプロパティとleftプロパティを使って配置してやる。right: 100%;その要素の右端が親要素の右端から100%、つまり左端になる。日本語にするとややこしい。positionプロパティの組み合わせを使った単純な絶対配置に留まらないブロックのレイアウトは色々応用が効くのできちんと憶えておいた方が良いと思う。特に最近流行りの擬似要素を利用したテクニックでは頻出するので、きちんと理解していないと何をやっているのかよくわからなくなってしまうだろう。

:after擬似要素の幅が10000pxのため、このままではもちろん水平スクロールバーが出てしまうので、

body {
  overflow-x: hidden;
}

とする必要もある。これで後は好きな背景を指定するのみ。繰り返し画像を背景画像にする場合は境目でのつながりも考慮してbackground-positionを工夫する必要もあると思う。

気をつけるのは上下に枠線を引く場合で、その場合は:before及びafter擬似要素のtop及びrightbottomleftプロパティが枠線の内側を基準にするので枠線のサイズ分位置をずらす必要がある。

pre {
  border-top: 3px double #ccc;
  border-bottom: 3px double #ccc;
  position: relative;
}

pre:before,
pre:after {
  border-top: 3px double #ccc;
  border-bottom: 3px double #ccc;
  display: block;
  position: absolute;
  top: -3px;
  width: 10000px;
  height: 100%;
  content: "";
}

上記のように上下に3pxの二重線を引きたいなら、擬似要素の位置を3pxずらしてやるということだ。


擬似要素2つ使わないでもたぶん実現出来るし、最終的にはみ出したのを隠すならば、margin: 0 -10000pxとかでも実現できそうだが面倒になったのでこれで。最近:before及びafter擬似要素があればなんでもできるんじゃないかと勘違いし始めていて危険な兆候だ……。擬似要素厨とかいう言葉が生まれる日も近い。

追記

Mobile Safari(やMac上のSafari)で横スクロールバーが出てしまうらしいbody要素へのoverflow-x: hidden;がうまく効かないというバグらしいので、body要素直下に全体を括るdiv要素を作り、そこでoverflow-x: hidden;とすれば回避できるらしいが、そうするくらいならJavaScriptでどうにかした方が良さそう。

また、body要素にposition: relative;を加えると直るという話もあったが、うちのサイトではうまくいかなかった。

他にはhtml要素にもCSS指定を追加するという方法も聞いた。

html {
  position: relative;
  width: 100%;
  height: 100%;
  overflow-x: hidden;
}

うちのサイトのスタイルシートに加えたところ、今度はOpera 11でスクロールバーが二重になるという現象が起きた(他のページでは再現できない)。

なんか他の方法考えてみる……。