レスポンシブ対応にメディアクエリなしで、CSSの関数で定義!border-radiusの値を変えるテクニック

border-radiusの値をデスクトップとスマホで変える、例えばビューポートが大きくてマージンがある場合は8pxで角丸にし、ビューポートが小さくてマージンがない場合は0pxで矩形にする。

メディアクエリで簡単に実装できると思うかもしれません。しかし、ビューポートのサイズが小さく、マージンがある場合に8pxの角丸になりません。

この難しい条件をCSSの関数で実装するテクニックを紹介します。

border-radiusの値をスマホとデスクトップで変えるテクニック

Conditional Border Radius In CSS
by Ahmad Shadeed

下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。

はじめに

私はデベロッパーがどのようなCSSを書いているか知りたいと常に思っています。少し前に、facebook.comのメインのフィードにあるカードコンポーネントに実装されているborder-radiusの値が非常に興味深いことに気がつきました。

私はこの小さな発見をツイートしました。

Miriam Suzanneからリプライがありました。

常に8pxですか?私にはその数学はトグルのように見えますが、ある時点((100vw - 4px) - 100%)でマイナスになり、9999から-9999に切り替わると、負の値になる可能性がありませんか?そうすると、値が0に反転するのでは?基本的に、ビューポートのフルサイズから4px以内であれば、角丸の半径は削除されます。

その数時間後、FacebookのFrank Yanからリプライがありました。

これはカードがビューポートの幅いっぱいに表示されているときに、8px0pxに反転させるための条件文です。

これは素晴らしいアイデアだと思いませんか?

私は最初、これは一種のバグか何かの間違いではないかと思いました。しかし、私は間違っていました。この記事では、その問題点を明らかにし、解決策がどのように機能するかを説明します。

border-radiusの値をスマホとデスクトップで変えるテクニック

border-radius8pxのカードコンポーネントがあるとします。カードにマージンがない場合、またはビューポートの幅いっぱいにカードが表示されている場合は、border-radius0pxに反転させたいと思います。

サイトのキャプチャ

左: スマホでは0px
右: デスクトップでは8px

これはCSSのメディアクエリでborder-radiusを削除することで実装できます。

しかし、これには限界があります。
何らかの理由で、ビューポートサイズが450px未満でborder-radiusを有効にしたい場合は、CSSクラスのバリエーションを作成し、再びメディアクエリで定義する必要があります。

実装方法の解説

それでは、Facebookのチームが実装した素晴らしいアイデアを真似て、実装してみます。

このロジックをCSSで実装するには、CSSの比較関数を使って2つの値を比較する必要があります。比較関数をご存じない方は、下記をご覧ください。

このテクニックは、Heydon Pickeringの記事「The Flexbox Holy Albatross」にヒントを得ています。これをFacebookのNaman Goelはborder-radiusで機能するようにしました。

上記のCSSについて詳しく見てみましょう。

  1. 0pxmin()の計算値があり、この2つの値をmax()関数が比較し、大きい方の値が使用されます。
  2. min()関数では、8pxcalc((100vw - 4px - 100%) * 9999)の計算値を比較します。これにより、非常に大きな正または負の数値が得られます。
  3. 9999は、強制的に0px8pxのどちらかにするための大きな数値です。

次に、calc()の魔法を説明しましょう。

魔法は値が100%の時に起こり、2つの異なるシナリオに基づいています。

  • その要素を含む要素(親やラッパーなど、CSSでの呼び名は問いません)の100%と等しくすることができます。
  • または、カードがビューポートの幅いっぱいに表示される場合(例えばスマホ)は、100vwと等しくすることができます。
calc()の魔法

calc()の魔法

なぜ、9999を使用するのか

この数字にすごいパワーがあるとか、そういうことではありません。エッジケースを避けるためです。このことを思い出させてくれたTemani Afifに感謝します。

ビューポートの幅が375pxで、コンテナの幅が365pxだとします。この値を式に代入すると、下記のようになります。

calc()の計算値は6pxになりますが、これは望んだ値ではありません。border-radiusの値には、0px8pxのどちらかにしたいのです。これを実現するために、結果を9999のように実際に使用される可能性が低い大きな数字で倍にします。

このCSSに基づいて、ブラウザはmin()関数から8pxを選び、次にmax()関数から同じ値を選びます。

最初のシナリオに基づいた例を見てみましょう。
幅が1440pxのビューポートがあり、カードコンポーネントは700pxのコンテナ内に配置されています。

最初のシナリオに基づいた例

最初のシナリオに基づいた例

結果の値に9999を掛けると7359264となり、大きな乱数となります。この場合、CSSはブラウザで次のようになります。

min()関数があるので、2つの値を比較して、最小値である8pxが選ばれます。そして、max()関数で、8pxの方が勝ちます。これが、このCSSの巧妙な使用例です。

次に、カードがビューポートの幅いっぱいに表示される場合です。これは、スマホのビューポートで見ることができます。コンテナの幅とビューポートの幅が同じであることに注目してください。

カードがビューポートの幅いっぱいに表示される場合

カードがビューポートの幅いっぱいに表示される場合(スマホ)

値に9999を掛けると-39996pxとなり、マイナスの値となります。CSSはブラウザで次のようになります。

さあ、お楽しみの時間です!
ブラウザは2つの質問をします。

  • どちらの値が小さいですか?
    8px-39996pxのどちらでしょうか?
    結果は、-39996pxです。
  • どちらの値が大きいですか?
    0px-39996pxのどちらでしょうか?
    結果は、0pxです。

これをみて、あなたはどう思いましたか?
CSSの比較機能をこのように巧妙に使用していることに、私は驚いています!

Temani AfifとLiad Yosefが提案したように、CSSのclamp()を使用することで、これを次のレベルに上げることができます。古いバージョンのSafari(v12など)ではサポートされていないので、Facebookチームは使用しなかったと思います。

実際の動作は、下記のデモでご覧ください。

See the Pen
Border radius / FB
by Ahmad Shadeed (@shadeed)
on CodePen.

終わりに

この記事があなたのお役に立てれば幸いです。
コメントや提案があれば、@shadeed9までお願いします。

sponsors

top of page

©2021 coliss