CSSポリ流体サイジングを使用した流体応答性タイポグラフィ
公開: 2022-03-10この記事では、それを別のレベルに引き上げます。 十分にサポートされているブラウザ機能といくつかの基本的な代数を使用して、複数のブレークポイントと事前定義されたフォントサイズにわたってスケーラブルで流動的なタイポグラフィを作成する方法を検討します。 最良の部分は、Sassを使用してすべてを自動化できることです。
SmashingMagの詳細:
- vhおよびvwユニットを使用した真に流動的なタイポグラフィ
- HTMLメールニュースレターデザインの活版印刷パターン
- Webタイポグラフィの良い例、悪い例、すばらしい例
- より意味のあるWebタイポグラフィのためのツールとリソース
Webページのデザインでクリエイティブデザイナーと協力する場合、ブレークポイントごとに1つずつ、複数のSketchまたはPhotoshopのアートボード/レイアウトを受け取ることはかなり一般的です。 その設計では、要素( h1
見出しなど)は通常、ブレークポイントごとに異なるサイズになります。 例えば:
- 小さなレイアウトの
h1
は22px
になる可能性があります - ミディアムレイアウトの
h1
は24px
である可能性があります - 大きなレイアウトの
h1
は34px
になる可能性があります
このための最低限のCSSは、メディアクエリを使用します。
h1 { font-size: 22px; } @media (min-width:576px) { h1 { font-size: 22px; } } @media (min-width:768px) { h1 { font-size: 24px; } } @media (min-width:992px) { h1 { font-size: 34px; } }
これは良い最初のステップですが、 font-size
を、提供されたブレークポイントで設計者が指定したものだけに制限しています。 「850px幅のビューポートでのfont-size
はどうあるべきですか?」と尋ねた場合、デザイナーは何と言いますか? ほとんどの場合、答えは24pxから34pxの間になるということです。 しかし、現時点では、CSSによると24pxであり、おそらくデザイナーが想定していたものではありません。
この時点でのオプションは、そのサイズを計算し、別のブレークポイントを追加することです。 それは簡単です。 しかし、他のすべての解像度はどうですか? font-size
は800px幅で何にする必要がありますか? 900pxはどうですか? 935pxはどうですか? 明らかに、設計者はすべての可能な解像度の完全なレイアウトを提供するつもりはありません。 たとえそうだったとしても、デザイナーが望むすべての異なるfont-sizes
に数十(または数百)のブレークポイントを追加する必要がありますか? もちろん違います。
レイアウトは、ビューポートの幅に合わせてすでに流動的にスケーリングされています。 あなたのタイポグラフィがあなたの流動的なレイアウトで予想通りにスケーリングされたら素晴らしいと思いませんか? これを改善するために他に何ができるでしょうか?
ビューポートユニットを救助する?
ビューポートユニットは、正しい方向へのもう1つのステップです。 レイアウトに合わせてテキストのサイズをスムーズに変更できます。 そして、ブラウザのサポートは最近素晴らしいです。
ただし、ビューポートユニットの実行可能性は、Webページの元のクリエイティブデザインに大きく依存します。 vw
を使用してfont-size
を設定するだけで、次のことができます。
h1 { font-size: 2vw; }
ただし、これは、クリエイティブアートボードがこれを考慮に入れている場合にのみ機能します。 デザイナーは、各アートボードの幅のちょうど2%のテキストサイズを選択しましたか? もちろん違います。 各ブレークポイントのvw
値を計算してみましょう。
22px
サイズ@ 576px
幅= 22⁄576 * 100 = 24px
サイズ@ 768px
幅= 24⁄768 * 100 = 3.13vw34pxサイズ@ 34px
992px
= 34⁄992 * 100 = 3.43vw
それらは近いですが、すべて同じではありません。 したがって、テキストサイズ間を移行するには、メディアクエリを使用する必要があり、ジャンプが発生します。 そして、この奇妙な副作用を考えてみましょう。
@ 767px、ビューポート幅の3.82%は29pxです。 ビューポートが1ピクセル広い場合、 font-size
は突然24ピクセルに戻ります。 サイズ変更されているビューポートのこのアニメーションは、この望ましくない副作用を示しています。
フォントサイズのこの劇的な変化は、ほぼ間違いなく、デザイナーが想定していたものではありません。 では、この問題をどのように解決するのでしょうか。
統計的線形回帰?
待って。 何? はい、これはCSSに関する記事ですが、いくつかの基本的な数学は、私たちの問題の洗練された解決策に大いに役立つ可能性があります。
まず、解像度と対応するテキストサイズをグラフにプロットしましょう。
ここでは、定義されたビューポート幅でのデザイナーの指定されたテキストサイズの散布図を見ることができます。 x軸はビューポートの幅で、y軸はfont-size
です。 その行を見ますか? これはトレンドラインと呼ばれます。 これは、提供されたデータに基づいて、任意のビューポート幅の補間されたfont-size
値を見つける方法です。
トレンドラインはこのすべての鍵です
このトレンドラインに従ってfont-size
を設定できれば、デザイナーが意図したものとほぼ一致するすべての解像度でスムーズにスケーリングするh1が得られます。 まず、数学を見てみましょう。 直線は次の方程式で定義されます。
- m =勾配
- b = y切片
- x =現在のビューポート幅
- y =結果の
font-size
傾きとy切片を決定するためのいくつかの方法があります。 複数の値が含まれる場合、一般的な方法は最小二乗近似です。
これらの計算を実行すると、トレンドラインの方程式が得られます。
これをCSSで使用するにはどうすればよいですか?
さて、これは数学でかなり重くなっています。 フロントエンドのWeb開発で実際にこのようなものをどのように使用しますか? 答えはcalc()
です! 繰り返しになりますが、非常によくサポートされているかなり新しいCSSテクノロジーです。
次のようなトレンドライン方程式を使用できます。
h1 { font-size: calc({slope}*100vw + {y-intercept}px); }
スロープとy切片を見つけたら、プラグを差し込むだけです。
注:ビューポート幅の1/100であるvw
単位として使用しているため、勾配に100
を掛ける必要があります。
これは自動化できますか?
最小二乗適合法を使いやすいSass関数に移植しました。
/// least-squares-fit /// Calculate the least square fit linear regression of provided values /// @param {map} $map - A Sass map of viewport width and size value combinations /// @return Linear equation as a calc() function /// @example /// font-size: least-squares-fit((576px: 24px, 768px: 24px, 992px: 34px)); /// @author Jake Wilson <[email protected]> @function least-squares-fit($map) { // Get the number of provided breakpoints $length: length(map-keys($map)); // Error if the number of breakpoints is < 2 @if ($length < 2) { @error "leastSquaresFit() $map must be at least 2 values" } // Calculate the Means $resTotal: 0; $valueTotal: 0; @each $res, $value in $map { $resTotal: $resTotal + $res; $valueTotal: $valueTotal + $value; } $resMean: $resTotal/$length; $valueMean: $valueTotal/$length; // Calculate some other stuff $multipliedDiff: 0; $squaredDiff: 0; @each $res, $value in $map { // Differences from means $resDiff: $res - $resMean; $valueDiff: $value - $valueMean; // Sum of multiplied differences $multipliedDiff: $multipliedDiff + ($resDiff * $valueDiff); // Sum of squared resolution differences $squaredDiff: $squaredDiff + ($resDiff * $resDiff); } // Calculate the Slope $m: $multipliedDiff / $squaredDiff; // Calculate the Y-Intercept $b: $valueMean - ($m * $resMean); // Return the CSS calc equation @return calc(#{$m*100}vw + #{$b}); }
これは本当に機能しますか? このCodePenを開き、ブラウザウィンドウのサイズを変更します。 できます! フォントサイズは、元のデザインが求めていたものにかなり近く、レイアウトに合わせてスムーズに拡大縮小されます。
確かに、それは完璧ではありません。 値は元のデザインに近いですが、完全には一致していません。 これは、線形近似曲線が特定のビューポート幅での特定のフォントサイズの近似値であるためです。 これは線形回帰の継承です。 結果には常にエラーがあります。 これは、単純さと正確さのトレードオフです。 また、テキストサイズが多様であるほど、トレンドラインにエラーが発生することを覚えておいてください。
これより上手くできますか?
多項式最小二乗適合
より正確なトレンドラインを取得するには、次のような多項式回帰トレンドラインなど、より高度なトピックを確認する必要があります。
今ではそれはもっと似ています! 私たちの直線よりもはるかに正確です。 基本的な多項式回帰方程式は次のようになります。
曲線が正確であるほど、方程式は複雑になります。 残念ながら、CSSではこれを行うことはできません。 calc()
は、このタイプの高度な数学を実行することはできません。 具体的には、指数を計算することはできません。
font-size: calc(3vw * 3vw); /* This doesn't work in CSS */
したがって、 calc()
がこのタイプの非線形数学をサポートするまでは、線形方程式のみに固執します。 これを改善するために他にできることはありますか?
ブレークポイントと複数の一次方程式
ブレークポイントの各ペア間の直線のみを計算している場合はどうなりますか? このようなもの:
したがって、この例では、 22px
と24pxの間の直線を計算し、次に24px
と24px
の間の直線を計算し34px
。 Sassは次のようになります。
// SCSS h1 { @media (min-width:576px) { font-size: calc(???); } @media (min-width:768px) { font-size: calc(???); } }
これらのcalc()
値には最小二乗適合法を使用できますが、2点間の直線であるため、計算を大幅に簡略化できます。 直線の方程式を覚えていますか?
ここで話しているのは2つのポイントだけなので、傾き(m)とy切片(b)を見つけるのは簡単です。
これのためのSass関数は次のとおりです。
/// linear-interpolation /// Calculate the definition of a line between two points /// @param $map - A Sass map of viewport widths and size value pairs /// @returns A linear equation as a calc() function /// @example /// font-size: linear-interpolation((320px: 18px, 768px: 26px)); /// @author Jake Wilson <[email protected]> @function linear-interpolation($map) { $keys: map-keys($map); @if (length($keys) != 2) { @error "linear-interpolation() $map must be exactly 2 values"; } // The slope $m: (map-get($map, nth($keys, 2)) - map-get($map, nth($keys, 1)))/(nth($keys, 2) - nth($keys,1)); // The y-intercept $b: map-get($map, nth($keys, 1)) - $m * nth($keys, 1); // Determine if the sign should be positive or negative $sign: "+"; @if ($b < 0) { $sign: "-"; $b: abs($b); } @return calc(#{$m*100}vw #{$sign} #{$b}); }
ここで、Sassの複数のブレークポイントで線形補間関数を使用します。 また、いくつかの最小および最大font-sizes
を投入しましょう:
// SCSS h1 { // Minimum font-size font-size: 22px; // Font-size between 576 - 768 @media (min-width:576px) { $map: (576px: 22px, 768px: 24px); font-size: linear-interpolation($map); } // Font-size between 768 - 992 @media (min-width:768px) { $map: (768px: 24px, 992px: 34px); font-size: linear-interpolation($map); } // Maximum font-size @media (min-width:992px) { font-size: 34px; } }
そしてそれはこのCSSを生成します:
h1 { font-size: 22px; } @media (min-width: 576px) { h1 { font-size: calc(1.04166667vw + 16px); } } @media (min-width: 768px) { h1 { font-size: calc(4.46428571vw - 10.28571429px); } } @media (min-width: 992px) { h1 { font-size: 34px; } }
CSSサイジングの聖杯?
これをすべて素敵なSassミックスインでまとめましょう(怠惰で効率的です!)。 私はこの方法を作り出していますポリ流体サイジング:
/// poly-fluid-sizing /// Generate linear interpolated size values through multiple break points /// @param $property - A string CSS property name /// @param $map - A Sass map of viewport unit and size value pairs /// @requires function linear-interpolation /// @requires function map-sort /// @example /// @include poly-fluid-sizing('font-size', (576px: 22px, 768px: 24px, 992px: 34px)); /// @author Jake Wilson <[email protected]> @mixin poly-fluid-sizing($property, $map) { // Get the number of provided breakpoints $length: length(map-keys($map)); // Error if the number of breakpoints is < 2 @if ($length < 2) { @error "poly-fluid-sizing() $map requires at least values" } // Sort the map by viewport width (key) $map: map-sort($map); $keys: map-keys($map); // Minimum size #{$property}: map-get($map, nth($keys,1)); // Interpolated size through breakpoints @for $i from 1 through ($length - 1) { @media (min-width:nth($keys,$i)) { $value1: map-get($map, nth($keys,$i)); $value2: map-get($map, nth($keys,($i + 1))); // If values are not equal, perform linear interpolation @if ($value1 != $value2) { #{$property}: linear-interpolation((nth($keys,$i): $value1, nth($keys,($i+1)): $value2)); } @else { #{$property}: $value1; } } } // Maxmimum size @media (min-width:nth($keys,$length)) { #{$property}: map-get($map, nth($keys,$length)); } }
このSassミックスインには、次のGithub要点にいくつかのSass関数が必要です。
- 線形補間
- マップソート
- リストソート
- リスト-削除
poly-fluid-sizing()
ミックスインは、ビューポート幅の各ペアで線形補間を実行し、最小サイズと最大サイズを設定します。 これを任意のSassプロジェクトにインポートして、その背後にある数学を知らなくても簡単に利用できます。 これが、このメソッドを使用する最後のCodePenです。
いくつかのメモ
- 明らかに、この方法は
font-size
だけでなく、任意の単位/長さのプロパティ(margin
、padding
など)にも適用されます。 目的のプロパティ名を文字列としてミックスインに渡します。 - ビューポートの幅とサイズの値のペアのSassマップは、
poly-fluid-sizing()
ミックスインに任意の順序で渡すことができます。 ビューポートの幅に応じて、マップを最小から最大に自動的に並べ替えます。 したがって、このようなマップを渡すことができ、それはうまくいくでしょう:
$map: (576px: 22px, 320px: 18px, 992px: 34px, 768px: 24px); @include poly-fluid-sizing('font-size', $map);
- この方法の制限は、混合ユニットをミックスインに渡すことができないことです。 たとえば、
3em
@576px
幅です。 Sassは、そこで数学的に何をすべきかを本当に知りません。
結論
これは私たちができる最善のことですか? ポリ流体サイジングは、CSSでの流体ユニットサイジングの聖杯ですか? 多分。 CSSは現在、非線形アニメーションと遷移タイミング関数をサポートしているため、 calc()
もいつかサポートする可能性があります。 その場合は、非線形の多項式回帰をもう一度確認する価値があります。 しかし、そうではないかもしれません…とにかく線形スケーリングが優れているかもしれません。
私は2017年の初めにこのアイデアを検討し始め、最終的に上記のソリューションを開発しました。 それ以来、私はいくつかの開発者がこのパズルの同様のアイデアと異なるピースを思い付くのを見てきました。 自分の方法とそこにたどり着いた方法を共有する時が来たと思いました。 ビューポートユニット。 Calc()。 サス。 ブレークポイント。 これらはどれも新しいものではありません。 これらはすべて、何年にもわたって存在しているブラウザ機能です(さまざまな程度のサポートがあります)。 私はまだ十分に調査されていない方法でそれらを一緒に使用しただけです。 毎日使用しているツールを見て、それらをより有効に活用してスキルセットを向上させる方法をすぐに考えてください。