このページの本文へ

これはすごい! メディアクエリなしでレスポンシブを実装するCSSトリック5選

2017年03月28日 05時00分更新

文●Andy Kirk

  • この記事をはてなブックマークに追加
本文印刷
メディアクエリーのようにビューポートではなく、コンテナの幅に応じてレイアウトを柔軟に調整するテクニックを紹介。calc関数を上手に活用した、マニアックなCSSトリックたちです。

はじめに、タイトルとは裏腹にこの記事は、メディアクエリ不要論を唱えたり、メディアクエリを批判したりするものではないことを伝えておきます。メディアクエリはとても使い勝手が良いので、私はいつもいろいろなことに使っています。とはいえ、メディアクエリでレスポンシブWebデザインにおける課題すべてを解決できるわけではありません。

要素の配置はビューポートではなくコンテナの寸法に基づいて変更するのが望ましい場合がよくあります。この課題を解決するためにエレメントクエリのコンセプトが誕生しました。とはいえエレメントクエリは実際まだ納得のいくものになっていないので、Mat Marquisはこのコンセプトにおける課題を示し、改良してcontainer queries(コンテナクエリ)としました。

しかし、残念なことにそれでもなお納得のいくものにはなっていません。

将来きっと改良され解決されるであろう、コンテナクエリの当面の課題に対処するための技法とテクニックを紹介します。

Flexboxでflex-wrapを使う

Flex-wrapを使うことで、コンテナ寸法へ対応する課題の多くを解決できます。たとえば、通常十分なスペースがある場合は、2つの要素を左右に並べて表示し、そうではない場合は上下に並べて表示することが望まれます。次のデモ「Responsive module – flexbox」で確認してください。

凝った技法はなく、flexboxでflex-wrapを使っただけですが、うまくいっています。もちろんflexboxは2カラムの作成だけではなくさらに多くのことに使えますが、例ではデモ用にシンプルにしています。このテクニックの主要な部分を簡単に示します。

<div class="container">
  <div class="img">...</div>
  <div class="content">...</div>
</div>

...
.container {
  display: flex;
  flex-wrap: wrap;
  flex-direction: row;
}

.container .content,
.container .img {
  flex: 1 0 12em;
  /* Change 12em to your breakpoint. */
}

正しく把握するためにはflex-growflex-shrinkflex-basisの理解が大切です。これらのプロパティの関係を理解するにはZoe Gillenwaterによる『3 pro tips for using flexbox』がとても参考になります。

The Fab Four Technique

widthmin-widthmax-widthcalcを使ってブレークポイントベースで幅を切り替える、いわゆる「The Fab Four technique」はRémi Parmentierによって考案されました。このテクニックは当初、レスポンシブEメール用に開発されましたが、通常のWebページでも簡単に使えます。実際、Thierryのデモにあるように、自動調整モジュール(self-adapting modules)の作成において新たな可能性を切り開きました。以下に例を示します。

{
  min-width: 50%;
  width: calc((25em - 100%) * 1000);
  max-width: 100%;
  /* Change 25em to your breakpoint. */
}

これがうまくいくのは、widthが%で指定されている場合、要素のコンテナ幅の%になっているためです。calc関数は指定されたブレークポイントと比較し、実際の幅がブレークポイント未満の場合は大きな正の数、ブレークポイントより大きい場合は大きな負の数、完全に一致する場合はゼロを生成します。大きな正の数の場合の幅はmax-widthの値とし、大きな負の数またはゼロの場合の幅はmin-widthの値とします。

さて、上の例ではブレークポイントが25emに設定されています。この場合フォントサイズが16pxだと400pxになります。コンテナが400px以上、言い換えるとブレークポイントと等しいかそれより大きい場合、widthの値は0または大きな負の数になります。つまり、(400 - 400 = 0) * 1000 = 0 or (400 - 401 = -1) * 1000 = -1000ということです。

こうした値に対してはmin-widthが適用され、結果として上の例の要素の幅は50%になります。一方コンテナが399px以下、言い換えればブレークポイントより小さい場合、widthの値は大きな正の数になります。つまり、(400 - 399 = 1) * 1000 = 1000ということです。

この場合max-widthが適用され、結果として幅は100%になります。下の図は、このプロセスを視覚的に分かりやすく示しています。

Diagram visualizing the Fab Four Technique

これから説明する4つのデモはこのテクニックをさまざまな方法で使い、コンテナの幅に対応して要素の幅を切り替えています。

フロートされた画像:全幅/部分幅

以下のデモでは「Fab Four Technique」をfloatと組み合わせて使い、コンテナの幅によって画像を全幅と半幅に切り替えました。「Responsive module – float」で確認してください。

上のflexboxの例と同様、このテクニックを使うと、幅が小さい場合は要素の配置を上下に並べて表示し、十分なスペースがある場合はフロート/ラップするように切り替えられます。

フロートされた画像:表示/非表示

先に紹介したテクニックを応用して計算結果の符号を反転させ、min-widthの記述を削除してオンオフの切り替えを実装しました。この方法は小さいコンテナ内で装飾的な要素が貴重なスペースを占めてしまうおそれがある場合、装飾的な要素を非表示にするのに役立ちます。次のデモ「Responsive module – float / hidden」で確認してください。

分かりやすくするためにコードで示します。

{
  /* Removed min-width since we want the width to be zero at this point and negative widths are treated as zero */
  /* Inverted the multiplier: */
  width: calc((25em - 100%) * -1000);
  max-width: 100%;
  /* Change 25em to your breakpoint. */
}

テキストと画像:オーバーレイ/スタック

次のデモ「Responsive module – overlaid / stacked」で確認してください。

先に紹介したテクニックと同じ感覚でdivを追加して画像上にテキストを引き上げましたが、画像が小さすぎてテキストに覆われてしまう場合、代わりにテキストを画像の下部にスナップします。このテクニックは少し複雑なので分かりやすくしてみました。

.pull {
  /* Pull the text up over the image by this much: */
  margin-bottom: -10em;
}

ここではネガティブマージンによって後続のコンテンツが引き上げられ画像にオーバーレイしています。とはいえ、コンテナ幅がブレークポイントを超える場合は無効にする必要がありますが、min/max-marginプロパティがないので「Fab Four Technique」では実現できません。

しかし、パディングがコンテナ幅の割合で%指定されているなら、幸いpadding-toppadding-bottomが要素の高さに影響します。これを知っていると、次のようにcalcを使ってコンテナ幅に基づいてゼロと「とても大きい数」で切り替わるpadding-bottom値を生成できます。つまり、padding-bottom: calc((30em - 100%) * 1000);のようになります。

これらの値を制限するmin/max-paddingプロパティがないので.pulldivには直接適用できません。解決策はpaddingの切り替えを疑似要素に実装して強制的に高さを変更し.pull要素にmax-heightを使って高さをネガティブマージンと同じ値に指定してマージンをうまく相殺することです。

.pull {
  /* Pull the text up over the image by this much: */
  margin-bottom: -10em;
  /* Don't allow this container to be larger than the same amount: */
  max-height: 10em;
  /* and hide any overflow, just to be on the safe side: */
  overflow: hidden;
}

.pull::before {
  content: "";
  display: block;
  padding-bottom: calc((30em - 100%) * 1000);
  /* Change 30em to your breakpoint */
}

グラデーションオーバーレイ効果は、backgroundにgradientが適用された疑似要素に、先に説明したようにオンオフの切り替えを適用して実現します。

.image::after {
  content: "";
  display: block;
  position: absolute;
  left: 0;
  top: 0;

  /* Gradient to make the text more legible: */
  background-image: linear-gradient(to bottom, rgba(0,20,30,0) 0%,rgba(0,20,30,0) 50%,rgba(0,20,30,1) 100%);

  /* Extra .5% to prevent bleed due to rounding issues: */
  height: 100.5%;
  /* Toggle gradient overlay at the same breakpoint as the 'pull': */
  width: calc((30em - 100%) * -1000);
  /* Change 30em to your breakpoint */
  max-width: 100%;
}

リストを切り詰める

最後のテクニックはPriority Plus pattern on CSS tricks(CSSトリックの優先順位パターン)に触発されて開発しました。このパターンのように洗練されてはいませんが、JavaScriptをまったく必要としないテクニックです。次のデモ「Truncating List」で確認してください。

再びここで「The Fab Four technique」を使いますが、今回だけはコンテナの幅ではなく高さに基づいて使います。

<div class="outer">
  <div class="inner">
    <div class="item">...</div>
    ...
    <div class="control">...</div>
  </div>
</div>
...
.outer {
  height: 2.25em;
  overflow: hidden;
}

.outer:target {
  height: auto;
}

外側のコンテナ(.outer)の高さは固定されていて:targetが付いている要素を除きオーバーフローはすべて非表示になります。

.inner {
  display: flex;
  flex-wrap: wrap;
}

内側のコンテナ(.inner)はflex-wrapが有効になったflexコンテナなので、要素がラップされると高さが増します。しかし、最初の行より下の要素は.outerコンテナがoverflow:hiddenに設定されていることで非表示になり、 切り詰めることができます。

.control {
  height: calc((2.25em - 100%) * -1000);
  max-height: 2.25em;
}

:target .control--open {
  display: none;
}

:target .control--close {
  display: block;
}

more/lessコントロールはコンテナの高さがブレークポイント(メインリンクの高さと同じ)を超過した場合のみ表示され、:targetのステートがコントロールの表示状況を決定します。

CSSでテキストの位置をスマートに指定

次のデモ「Responsive Text Align」で確認してください。

テキストの長さとコンテナ内の利用可能なスペースとの比較に基づいて、テキストを中央揃えまたは左揃えにする、とても便利な方法です。Vijay Sharmaが考案したこのテクニックはとても簡単に実現できます。

優れた技法「Flex-grow 9999 Hack」

Joren Van Heeによる優れた技法「flex-grow 9999 hack」はこの記事にぴったりです。

Vasilis van Gemertの「Look, No Media Queries」も必見

Vasilis van Gemertの動画「Look, no media queries」がきっかけで、メディアクエリなしでのレスポンシブデザインを研究し、この記事を書くことになりました。彼の動画は一見の価値があります。この記事のテーマにはあまり関わりがないものの、役に立つ別のアイデアも紹介されています。

最後に

エレメント/コンテナクエリを使わなくても、たくさんのことが実現できます。colorの値をはじめfont-size、line-height、border、box-shadow、margin、paddingなど挙げれば長くなります。これらすべては親コンテナの状態に従ってエレメント/コンテナクエリで調整できるのが望ましいのですが、残念なことに、すぐに実現する気配はまったくありません。とはいえ当面は、この記事で紹介したテクニックが役に立てば幸いです。

エレメントクエリを使ったデザインとはどのようなものか知りたい人は、はじめに『レスポンシブの常識が変わる!? 君はエレメントクエリーを知っているか?』を参考にしてください。

エレメント/コンテナクエリについてさらに詳しく知りたい人に役立つ資料を、以下に挙げておきます。

(原文:Responsive CSS Patterns without Media Queries

[翻訳:新岡祐佳子/編集:Livit

Web Professionalトップへ

WebProfessional 新着記事