見出し画像

CSS比較関数を用いたレスポンシブについて

clamp関数やmin関数、max関数など、近年の主要ブラウザで使えるようになってきたCSS比較関数。思ったより使用例の解説が見つからないので、BLUE B NOSEで実際に取り入れていることや、最近発見したclamp関数の入れ子なども、実体験を交えてご紹介します。

近年主要なブラウザでサポートされるようになり、気軽に使えるようになったCSS比較関数、clamp()、min()、max()ですが、有効活用できていますか?
BLUE B NOSE(以下:BBN)では推奨とされていなさそうな使い方も含めて積極的に採用し、少しずつ知見を蓄積しています。筆者は『EVERY LAYOUT』に感化されて使い始めたCSS比較関数と、同関数を用いたレスポンシブについて、簡単にご紹介します。


黒船だった、『EVERY LAYOUT』

筆者は別の記事

でも『EVERY LAYOUT』の影響を述べていますが、CSS比較関数についても、『EVERY LAYOUT』で紹介されていて試しに取り入れてみたのがきっかけだったりするので、最近の影響としては本当にエポックメイキングというか、黒船に近い『EVERY LAYOUT』なんですが、筆者が観測する範囲では彗星のように現れて彗星のように消えていった感もあり、その残滓は筆者の中に留まるのみと言った寂しさもあるので、改めてその影響力が大きかったことを簡単に記しておきますね。

さて、本題ですが今回触れるCSS比較関数は、clamp関数、min関数、max関数の3つ。
clamp関数は、

最小値、推奨値、最大値の順

https://developer.mozilla.org/ja/docs/Web/CSS/clamp より引用

で指定する関数で、min関数は

カンマで区切られた式のリストから最小の (最も負である) 値を設定

https://developer.mozilla.org/ja/docs/Web/CSS/min より引用

できる関数、max関数は

CSS プロパティの値としてカンマで区切られた式のリストから最大の (最も正である) 値を設定

https://developer.mozilla.org/ja/docs/Web/CSS/max より引用

する関数です。
min関数とmax関数は名前の印象に反して、実際は前者が最大値を設定する関数、後者が最小値を定める関数といったイメージ。

min(50vw, 200px)

max(40%, 300px)

のように動的な値を前に指定して、後者を固定のサイズとしておくと、minの場合は固定のサイズが上限、maxの場合は固定した値が下限になるようなイメージです。具体的には自分で弄ってみて調整してみる、色々試して掴んでみないと使えるようにはならない関数です。

上手に値を設定するだけでもレスポンシブに使える比較関数ですが、「動的な値」の方を工夫することで、

@media query(min-screen){
  hoge: fuga;
}
@media query(max-screen) {
  hoge: fuga;
}

で指定するような使い方も可能となります。

数値で調整可能なプロパティは、比較関数で

例えば、会社概要の表やお問い合わせフォームのように、田の字型というか2列の表を実装する時、小さい画面では1列に変形させたいからと、tableタグで工夫したり、dlタグで表組っぽく組み上げたところで、以下のような設定を組んだとします。

border-width: 1px clamp(0px,(960px - 100vw) * 999,1px) 0 1px;

clamp関数を使って、border-widthを0pxから1pxへ変化するように指定しているのですが、推奨値の部分を`(960px - 100vw) * 999`としています。100vwと960pxの関係が変化した時に、border-widthが0pxになるか1pxになるかが変化しそうな気はしますよね?

`960px-100vw`が1pxにでもなれば、999を掛け算するので最大値の1pxが適用されます。100vwが960pxより小さい場合、つまり

@media query(max-screen: 960px){
   border-right-width: 1px;
}

と指定するのとほぼ同じ作用になります。ただ、注意すべきポイントはclamp関数を用いる際は値が0であっても、`0px`と単位もきちんとつけること。そうでないと、Sassのコンパイル等で失敗します。

このように、パラメーターを数値で調整する箇所については、clamp関数(場面によっては、min関数やmax関数)を用いることで、今まで

@media query(min-screen){
  hoge: fuga;
}
@media query(max-screen) {
  hoge: fuga;
}

と記述していた部分を少しスッキリさせる事が可能です。

メンテナンス性、分かりやすさで言えばメディアクエリを用いた方が直感的で、難しく考えることなく使えるのですが、それまでのスタイルを打ち消して適用するようなCSSになるよりは多少小綺麗というか、気持ち良いのかなぁ、という程度の利点はありそうです。

ちょっとトリッキーな組み合わせも

デモサイト、BX-05

のヘッダーでも使用している、「フォーカスが当たると入力欄が伸びる」、サイト内検索用の入力フォームですが、`:focus`の場合に

width:calc(100vw - 2rem);
max-width:max((520px - 100vw) * 999,350px);

と設定しています。
widthの方は簡単ですよね。100vwより左右に1remずつ小さいサイズにしなさいという指定です。
これに対してmax-widthにmax関数を使用しています。max関数は下限、最小値を固定する働きをします。つまり、`520px - 100vw`で1pxになれば、`max-width:350px`が適用されます。`width: calc(100vw - 2rem)`を指定している要素に対して、です。

どんな挙動をすると思います? 正解は100vwが519px以下の時は`width: calc(100vw - 2rem)`、100vwより左右に1remずつ小さいサイズが適用される、です。100vwが520pxを超えた時、max-widthの350pxが有効になるという記述です。

@media query(min-screen: 520px){
  width: 350px;
}

としても良いんですが、max-widthの適用タイミングをmax関数で制御することで、メディアクエリを書かずに解決するという手法です。

今度は、`display:flex`を割り当てた要素の事例ですが、BX-00

のフッターで使用しているテクニックです。

『EVERY LAYOUT』のSidebarのテクニックを応用して、一定のサイズ以下で左右に並んでいた要素が、`flex-wrap:wrap`により上下のレイアウトに変化する挙動に一工夫加え、大きな画面では`float: right`に近い挙動、小さい要素が右寄せになる仕様です。

`display:flex`を当てた子要素、左側に配置したいサイズを指定しにくい要素に対して、

flex-basis: calc((520px - 100vw)*999);
min-width: fit-content;

と、min-widthを指定します。右側に配置したい要素には、

min-width: min((520px - 100vw) * 999,100%);
margin-left: auto;

を指定しました。

100vwが520pxとなる前後で表示が変化するのはお分かりになると思いますが、`margin-left:auto;`を指定した要素のmin-widthが520px以下の画面では100%になります。同時に、flex-basisを設定した方も、値が極大化するので、実質100%の幅を獲得します。
この時、`min-width:fit-content;`としておくことで、100vwが520pxより大きい時でも最小幅は`fit-content`として確保されるので、大きく崩れることはありません。

このように、max関数やmin関数と、`max-*`や`min-*`と組み合わせて頭の体操に打ち勝てれば、単純に`@media query`でレスポンシブを考えるより、柔軟な発想でCSSを書くことができます。

clamp関数の入れ子も可能!

厳密にはclamp関数だけでなく、min関数やmax関数でも可能だと思いますが、BBNでは主にコンテンツのメイン部分、大きな要素のwrapper代わりに指定している左右のpaddingについて、clamp関数の入れ子を採用しています。

clamp(
  clamp(16px,(100vw - 960px) * 999,40px),
  (100vw - 520px) * 999,
  clamp(40px,(100vw - 960px) * 999,104px)
)

とし、最小値の設定にclamp、最大値の設定にもclampを入れ子にすることで、最小値も最大値も変化するため、16px、40px、104pxと変化する多段階の設定値切り替えを実現しています。

これを`@media query`で実現するとなると結構な記述量になりますが、clamp関数の入れ子にすることで一回の記述で済むという利点があります。ただ、これと連動、相殺するような負の動きも考えるとなると、最小、最大の組み合わせが逆になってしまうので、頭の体操としてはそこそこの難易度になるという、コーディングする側へのデメリットもあります。

Sassを組み合わせると便利

公式にはあまり推奨されていなさそうな、100vwと特定のビューポートサイズを組み合わせた`@media query`代わりのCSS比較関数を用いたレスポンシブですが、Sass、特にDart-Sassのmapを組み合わせると、やや長い記述が多少スッキリ、直感的な書き方へ変えられます。

BBNは、サイズを制御するファイル上で

$breaks: 521px;
$breakm: 961px;
$unders: $breaks - 1px;
$underm: $breakm - 1px;
$breakpoints: (
  unders: calc(($unders - 100vw) * 999),
  underm: calc(($underm - 100vw) * 999),
  overs: calc((100vw - $unders) * 999),
  overm: calc((100vw - $underm) * 999),
) !default;

@function breakpoints($key) {
  @return map.get($breakpoints, $key);
}

と、mapから値を取得するfunctionも設定(厳密には、calcは不要。あった方が分かりやすい?)し、clamp関数やmin関数、max関数の中で、

border-width: 1px clamp(0px, setting.breakpoints(underm), 1px) 0 1px;

のように記述しています。これなら、「あれ、どうだっけ?」をあまり悩まずに書けるでしょ?

従来の`@media query`と組み合わせて、より柔軟なレスポンシブWebデザインが可能となります。

モダンな書き方、プロパティを学ぶきっかけにも

CSS比較関数を使えば、いわゆるカード型のリンク要素を1つ並び、2つ並び、3つ並びへ変化させるようなスタイリングでも、

display: flex;
flex-wrap: wrap;
gap: 39px 16px;

を指定した親要素に

width: clamp(50% - 8px,(429px - 100vw) * 999,100%);
max-width: max((768px - 100vw) * 999,(100% - 32px) / 3);

と、width、max-widthを調整した子要素を組み合わせたり、

--gutter: 24px;

display: grid;
gap: var(--gutter);
grid-template-columns: 
  repeat(auto-fill, minmax(
   clamp(
    clamp((100% - var(--gutter) * 2) / 3,(960px - 100vw) * 999,50% - var(--gutter) * .5),(960px - 100vw) * 999,
    clamp(50% - var(--gutter) * .5,(520px - 100vw) * 999,100%)
    ),
  1fr)
);

とgrid指定にしつつ、カスタムプロパティを後から変更しやすい形で構築する、といったことも可能になります。(もちろん、4つ並びというのもパパッとできます)

`@media query`だけしか使えないと、CSS比較関数で短く書けるところも膨大な記述が必要となり、手間や工数、面倒臭さも増大します。

色んなプロパティを上手に組み合わせて、「こうやればできる」を新たに考えるのは確かに面倒で、CSS比較関数も組み合わせた少々トリッキーな書き方というのは、頭の体操もハードになりますが、モダンな書き方や新たなプロパティ、知らなかったものを学ぶきっかけとなったりします。

2020年代も後半に入ろうとしている今、いつまでも「IEの亡霊」に囚われず、使えるようになったモダンな書き方、モダンな手法は、"caniuse"や先進的な人たちをチェックした上でどんどん導入していっても良いのではないでしょうか。

BBNでは、新しいことにも挑戦しています

Web制作の現場に関わること10年超のメンバーで運営するBBNは、旧来の手法に囚われることなく、革新的なアプローチを追求しています。

私たちは、良いと思ったことやパフォーマンスを向上させるものは積極的に取り入れ、CSSやその他の技術に限らず、日本語ではあまり情報を得られない分野であっても、果敢に取り入れ、成功した経験や失敗した経験を積極的に共有しています。

今回のようなモダンな関数やプロパティを使ったトリッキーなテクニック、あるいはまだあまり一般的でないテクニックに興味をお持ちであれば、ぜひBBNと一緒にお仕事してみませんか? 本記事に関する疑問や質問、協業に関するご相談等ございましたら、いつでも気軽にお問い合わせください。

今後も「お役立ち情報」をお届けします

BLUE B NOSEでは今後も、経験を通じて得たナレッジの共有や、独自視点でのお役立ち情報をお届けします。HP上でもコンテンツを発信する予定なので、もしよろしければ当アカウントのフォローや、HPのチェックもお願いします。


この記事が気に入ったらサポートをしてみませんか?