コンテナからの解放。

今年もCSSには大変お世話になりました。なかでも今年になってとくにお世話になる頻度が増えたのが「calcキャルク()関数」。プロパティの値に、計算式を使わせてくれるスゴいやつです。ここでは、そんなcalc()関数を使った便利ワザについて書いてゆきます。
この記事は「今年お世話になったCSS Advent Calendar 2016」の23日めの記事です:)

calc()関数を使った便利なスタイル

例えば、下図にみたいな「コンテンツが画面中央に配置されていて、サイト全体のコンテンツは横幅980px内に収まるんだけど、その中のあるセクションだけ背景色がウィンドウの端っこまで広がってる」なんてデザインの場合。以前は、「サイト全体を包括するコンテナには横幅を指定しないで、セクションごとに横幅を指定」していたと思うんです。

HTMLとCSSは以下のように、article要素には横幅を指定しないで、ウィンドウ幅まで広がらないsection要素にだけ、横幅をキメたスタイルを指定するカンジです。

<article>
	<h1 class="container">今年お世話になったCSS</h1>
	<p class="container">横幅980pxで中央配置のコンテンツ。</p>
	<section class="container">
		<h2>広がらないセクション</h2>
		<p>Lollipop icing chupa chups macaroon. Macaroon marshmallow candy sesame snaps...</p>
	</section>
	<section>
		<div class="container">
		<h2>横幅いっぱいまで広がるセクション</h2>
		<p>Cupcake bonbon apple pie pastry muffin muffin oat cake. Brownie macaroon...</p>
		</div>
	</section>
	<section class="container">
		<h2>広がらないセクション</h2>
		<p>Lollipop icing chupa chups macaroon. Macaroon marshmallow candy sesame snaps...</p>
	</section>
</article>
.container {
	width: 980px;
	margin: 0 auto;
}

下記記事を見て、世界は一変しました。

Breaking Out With Viewport Units and Calc(2016.5.26)

こちらの記事の中で紹介されている計算式はとっても単純で、calc()を使って、以下のようにスタイルを指定します。すると、HTMLにはウィンドウ幅まで広がるsection要素にだけ、スタイルを指定すればよくなるんです:D

.breaking-out {
	margin-right: calc(50% - 50vw);
	margin-left: calc(50% - 50vw);
}
<article class="container">
	<h1>今年お世話になったCSS</h1>
	<p>横幅980pxで中央配置のコンテンツ。</p>
	<section>
		<h2>広がらないセクション</h2>
		<p>Lollipop icing chupa chups macaroon. Macaroon marshmallow candy sesame snaps...</p>
	</section>
	<section class="breaking-out">
		<h2>横幅いっぱいまで広がるセクション</h2>
		<p>Cupcake bonbon apple pie pastry muffin muffin oat cake. Brownie macaroon...</p>
	</section>
	<section>
		<h2>広がらないセクション</h2>
		<p>Lollipop icing chupa chups macaroon. Macaroon marshmallow candy sesame snaps...</p>
	</section>
</article>

ページトップへ戻る

コンテナからの解放

このスタイルが何をしているのか詳しく見てゆきます。と言ってもとっても明快です。

.breaking-out {
	margin-right: calc(50% - 50vw);
	margin-left: calc(50% - 50vw);
}

異なる2種類の単位が使われてます。
ひとつめの「%」は、親要素を基準とした割合を表す単位。ふたつめの「vw」は、ビューポート(ウィンドウ)の横幅を基準とした割合を表す単位。その計算結果を、左右のマージンに適用しています。小さな値から大きな値を引くことになるので、左右にはネガティブマージンが適用されることになります。
「要素の横幅の半分」から「ウィンドウの横幅の半分」を引いた分ずつ。ちょうど、article要素とウィンドウ端までの狭間を埋めるように、要素を左右に引っ張ってるわけですねー明快:D

「ウィンドウ幅からコンテンツ幅を引いて2で割った分」なのだから、考え方はとってもシンプル。
<section class="breaking-out">
	<h2>ウィンドウ幅いっぱいまで広がるセクション</h2>
	<p>広がってるのが見やすいように背景に色をつけてみました。ご覧の通り、section要素は横幅いっぱいに広がりました!けれども、文章もなんもかも広がっちゃってちょっとみっともないですね…。</p>
</section>

ウィンドウ幅いっぱいまで広がるセクション

広がってるのが見やすいように背景に色をつけてみました。ご覧の通り、section要素は横幅いっぱいに広がりました!けれども、文章もなんもかも広がっちゃってちょっとみっともないですね…。
セクションの中身だけ元の横幅に戻すには、さらに次のセクションのように指定します。

背景だけ広がるセクション

ネガティブマージンで左右に広げた分と同じだけ内側に戻すため、今度は逆に、ウィンドウの横幅の半分から要素の横幅の半分を引いた分ずつ、左右にパディングとして指定します。

.section-container {
	padding-right: calc(50vw - 50%);
	padding-left: calc(50vw - 50%);
}
<section class="breaking-out section-container">
	<h2>背景だけ広がるセクション</h2>
	<p>ネガティブマージンで左右に広げた分と同じだけ内側に戻すため、今度は逆に、ウィンドウの横幅の半分から要素の横幅の半分を引いた分ずつ、左右にパディングとして指定します。</p>
</section>
セクションの幅は広がれど、%は親要素が基準というとこがポイント。

特定の要素だけ広げる

他にも、特定の要素に指定すれば、その要素だけウィンドウ幅に広げることができます。
次の要素ではimg要素を括るfigure要素に対して適用しています。img要素にはmax-width: 100%と指定することで、画像のオリジナルサイズに達するまでは親要素の横幅いっぱいまで伸縮するようになります。img要素にwidth/height属性の指定があると、横幅しか伸縮しません。height: autoも併せて指定することで、width/height属性の指定があっても、縦横比を維持して伸縮するようになります。

<section>
	<figure class="img breaking-out">
		<img src="img/event_header_bg.png" alt="" width="2000" height="400">
	</figure>
</section>
.breaking-out > img {
	max-width: 100%;
	height: auto;
}

要素で括らずに直接img要素に対して適用する場合には、max-width: 100%の指定があると親要素より大きくならないので、max-width: 100vwに変更してます。

<section>
	<img src="img/event_header_bg.png" alt="" width="2000" height="400" class="breaking-out">
</section>
img.breaking-out {
	max-width: 100vw;
	height: auto;
}

ページトップへ戻る

スクロールバー問題

記事によると、この方法だと、スクロールバーが見える状態の場合に、水平スクロールバーが表示されるようになっちゃうとのこと。vw単位は、スクロールバーの領域も含めた横幅なので、垂直スクロールバーが表示されてるとその分誤差が出ちゃうんですね。
なので、html要素とbody要素に対して、X方向のみスクロールバーが出ないよう指定しておく必要があります。
※大抵のブラウザではhtml要素かbody要素どちらか片方に指定すれば問題ないのですが、Safariでは、どちらか片方だとまだ横にスクロールできちゃうので、Safariのために両方に指定しておくとよいです。

html, body {
	overflow-x: hidden;
}

※このページでは、html/body要素ではなく、ページ全体を包む<div id="wrapper">〜</div>overflow: hiddenを指定しています。

ブラウザ検証

下記、検証用のページをいろんなブラウザで見てみましょう。

calc()とビューポート単位を使ったコンテナからの解放(検証用)

Chrome、Safari、Firefoxは問題なく広がってます。けどやっぱりWindowsで見ると、スクロールバーの分だけ誤差があるため、Macで見るよりネガティブマージン多めの印象ですね…。

Mac Chrome 55
Mac Safari 10
Windows 10 Firefox 50
Windows 10 Edge 14
Windows 10 IE 11
Windows 7 IE 9

さいごに

calc()関数には他にも、等分割で横並びの時の余白とか、アイコンを要素の上下中央に絶対配置する時とか、いろんな場面でいろんな面倒がハショれて大変重宝しました。
ただ、Internet ExplorerとEdgeでは、100% / 3など割り切れない計算の時に、ちょっと増えちゃうみたいで、そこだけ割り切らなきゃ←ですけれど…:(。 2017年も引き続きお世話になると思います:)

以上、_watercolorの、「今年お世話になったCSS Advent Calendar 2016」でした。
最後まで読んでいただきありがとうございました!

ひーろがーれよ♪ʺପ(๑бωб)੭。◌⑅⃝*॰ॱ

100% / 7
100% / 7
100% / 7
100% / 7
100% / 7
100% / 7
100% / 7

Comment & Pingback

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください