0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

rfsでのサイズ指定を直感的にするsass function

Last updated at Posted at 2021-11-05

画面サイズに応じてフォントサイズ(などのプロパティ)が変わるスタイルライブラリのrfs。
公式の言葉を借りれば、「CSS プロパティを様々なデバイスでレスポンシブルにスケールできる、Bootstrap のリサイジングエンジンです。」。
Bootstrapの一部ですが、単独のライブラリとして提供されているので、ときどき使っています。

こちらを使ってフォントサイズなどを指定するのですが、デフォルトではビューポートの幅が1200pxである前提で計算されるので、モバイル版でフォントサイズを指定するときに直感的に作業できなくて困っていました。これを、ビューポートの幅をもとに指定すべきサイズを計算して指定できるようなfunctionを作りました。(他にまっとうな方法があるのかもしれないが…)

まず完成形はこちらです。Dart Sassです。
rfsの各種設定を初期値のまま運用する前提で計算しています。小さいサイズについてはそのまま値を返すようにしています。

完成

scss
@use "sass:math";
@use "../node_modules/rfs/scss" as rfs;

@function rfs-font-size($size, $viewport, $breakpoint:1200) {
	$result: math.div($size, $size * 0 + 1);
	@if math.div($size, 1.25) > 16 {	
		$result: 10 * math.div($breakpoint * $size - 18 * $breakpoint + 18 * $viewport, $breakpoint + 9 * $viewport);
	}
    @return $result;
}

@media (min-width: 320px) {
    //意図したサイズにしたいビューポートの幅を2つめの引数に指定
    @include rfs.font-size(rfs-font-size(34px, 320));
}

かんたんな解説

このfunctionで返った値をfont-sizeなどのmixinに渡すだけです。
rfsの初期値を変更している場合ですが、変更しそうな設定は以下のものかと想像します(変えたことはない)。

scss
// Base value
$rfs-base-value: 1.25rem !default;
$rfs-unit: rem !default;

// Breakpoint at where values start decreasing if screen width is smaller
$rfs-breakpoint: 1200px !default;
$rfs-breakpoint-unit: px !default;

// Factor of decrease
$rfs-factor: 10 !default;

// 1 rem = $rfs-rem-value px
$rfs-rem-value: 16 !default;

このうち、$rfs-breakpointの値については変更に対応しています(その他の値の変更にも(たぶん)対応しているバージョンも作りました)。
$rfs-breakpoint を変更した場合は、3つめの引数にその値を指定してください。

scss
@use "../node_modules/rfs/scss" as rfs with (
        $rfs-breakpoint: 768px
    );

@media (min-width: 320px) {
    @include rfs.font-size(rfs-font-size(34px, 320, 768));
}

ほかの変数も指定できるバージョン(2021/11/05追記)

ほかの変数も指定できるものも作りました。

scss
@function rfs-font-size2($size, $viewport, $breakpoint:1200, $rem:16, $base:1.25, $factor:10) {
	$result: math.div($size, $size * 0 + 1);
	@if math.div($size, $base) > $rem {
		$rb: $rem * $base;
		$v1: $breakpoint * ($rb * (-$factor) + $rb + $factor * $size) + $viewport * $rb * ($factor - 1);
		$v2: $breakpoint + $viewport * ($factor - 1);
		$result: math.div($v1, $v2);
	}
	@return $result;
}

rfsについて

rfsでは、各種サイズをこのように指定します。

scss
@use "../node_modules/rfs/scss" as rfs;

@media (min-width: 320px) {
    @include rfs.font-size(34px);
}

設定にもよりますが、基本的にはこのように、remvwcalcで足し合わせた値としてコンパイルされます。

css
@media (min-width: 320px) {
    font-size: calc(1.3375rem + 1.05vw);
}

「ブレイクポイントを設定して、その中間ではフォントサイズをスケールさせたい」というようなときにどうも上手く設定できずに困っていました。

  • デバイスの幅が767px以下では、フォントサイズを34pxに
  • デバイスの幅が768px以上では、フォントサイズを75pxに

というような指定をしたい場合です。
何も考えずに以下のように指定しますが、意図した結果になりません。

scss
@use "../node_modules/rfs/scss" as rfs;


.my-article {
    @include rfs.font-size(34px);
    @media (min-width: 768px) {
       @include rfs.font-size(75px);
    }
}

これは、このようにコンパイルされます。

css
.my-article {
    font-size: calc(1.3375rem + 1.05vw);
}
@media (min-width: 768px) {
    .my-article {
       font-size: calc(1.59375rem + 4.125vw);
    }
}

それぞれの実質的なフォントサイズは、ビューポートの幅によって決まるので、以下のようになります。

画面幅 @media 指定サイズ コンパイル結果 実質サイズ
320px 34px 1.3375rem + 1.05vw 24.76px
414px 25.747px
768px min-width:768px 75px 1.59375rem + 4.125vw 57.18px
1024px 67.74px
1200px 75px

この実質サイズはrfsのブレイクポイントの初期設定が1200pxであることから来ています。
vwの値を1200として計算すると、おおむね指定のフォントサイズになります(表の最終行)。

functionの解説

rfsではだいたい以下のような計算でサイズ(最後の$font-size)が決まるようです。

scss
$size: 34; //指定したサイズ

$rem: 16;
$value: $rem * 1.25;
$factor: 10;
$breakpoint: 1200;
$min: $value + ($size - $value) / $factor;
$diff: $size - min;

$font-size: "calc(#{($min / $rem)}rem + #{$diff * 100 / $breakpoint})vw)";

calcの中の式も展開すると下記のようになります。
ビューポートの幅$viewportは表示される環境によって変わりますが、仮に320pxとしています。

scss
$size: 34;
$breakpoint: 1200;
$viewport: 320; //ビューポートの幅

$font-size: (20 + ($size - 20) / 10) + ($size - (20 + ($size - 20) / 10)) * 100 / $breakpoint * $viewport / 100;

このような計算になります。

これを方程式として、目的の$sizeとなるような $font-sizeの値を求めます。
数学っぽく、変数をxy定数をabとして、以下のように書き換えると、このようになります。

x = (20 + (y - 20) / 10) + (y - (20 + (y - 20) / 10)) * 100 / a * b / 100
x = (20 + \frac{y - 20}{10}) + (y - (20 + \frac{y - 20}{10})) × \frac{100}{a} × \frac{b}{100}

これをyについて解きます。
計算はこちらのサイト(Wolfram|Alpha)でやりました。
いわゆるマジックナンバーだらけですが、よしとします。

y = 10 * (a * x - 18 * a + 18 * b) / (a + 9 * b)
y = \frac{10(ax - 18a + 18b)}{a + 9b}

scss表記に戻すとこうなります。(除算のところは適宜math.div()で置き換えてください)

$font-size: 10 * ($breakpoint * $size - 18 * $breakpoint + 18 * $viewport) / ($breakpoint + 9 * $viewport);

ほかの変数も指定できるバージョンの解説(2021/11/05追記)

$rem * $basec$factordとして書き換えると、このようになります。

x = (c + (y - c) / d) + (y - (c + (y - c) / d)) * 100 / a * b / 100
x = (c + \frac{y - c}{d}) + (y - (c + \frac{y - c}{d})) × \frac{100}{a} × \frac{b}{100}

これをyについて解きます。計算は上記と同じサイトで。

y = (a * (c * (-d) + c + d * x) + b * c * (d - 1)) / (a + b * (d - 1))
y = \frac{a (c (-d) + c + d x) + b c (d - 1)}{a + b (d - 1)}

scss表記に戻すとこうなります。(除算のところは適宜math.div()で置き換えてください)
cのところを$rem * $baseにしています。鬼長いので、ちょっと分割しています。

scss
$rb: $rem * $base;
$v1: $breakpoint * ($rb * (-$factor) + $rb + $factor * $size) + $viewport * $rb * ($factor - 1);
$v2: $breakpoint + $viewport * ($factor - 1);
$font-size: $v1 / $v2;
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?