はじめに
毎回微妙に違うけど似たようなロジックを、プロジェクト毎に組んでしまいがちです。
どうせいつも使うなら、自分用に npm package
化し、方針変更をバージョンアップで受け取れるようにしておく方が、理にかなってるような気がしたので、今回学習と実験を兼ねて作ってみました。
簡単に作れそうなものとして、css の MediaQueries
を題材に選んでみました。
おそらく似たようなpackageは大量に存在しているとは思っていますが、自力で車輪を発明することも今回の趣旨なので、ご容赦を。
大枠の方針
-
link要素
での使用は禁止。 -
.class
内で細かく分岐させる。 - breakpointは 任意の数を定数で管理。プロジェクトに応じて簡単にoverride可能にする。
- MediaQueries用のラッパー関数を中継して記述。生では書かない。
- mobile first / desktop first のどちらの方針も対応できる。
-
window.matchMedia
を使い、可能な限り似たロジックでjs
版もつくる。
今回作った2つの npm package
scss用とjs用として2つに分割して作成。
ほぼ自分用の側面が強いので名前を scoped modules
にします。
(統合する方針で当初構想したが、そもそも、 MediaQueries 管理をしないと行けない状況は最終手段感がある、 命名が完全には揃えられない、使うべき理由がそもそも大きく異なるはず、等の理由で分けて扱う事に。)
原則scssで完結できる世界を願い、jsの方はおまけです。
mq-scssの方針
cssコーディングは、絵を扱うという性質上、まま理不尽な状況が多い気がします。
また webサイト なのか webアプリ なのかといった違いで、求められるブレイクポイントの粒度が全然異なります。
これらの事情を鑑み、安全に運用するために、グローバルなbreakpoint管理
と イレギュラーな状況に対応できる
の2点を同時に担保できる方法を目指します。
インストール
$ npm i -D @rm-labo/mq-scss
@import 'path/to/node_modules/@rm-labo/mq-scss/_mq.scss';
JavaScriptのエントリポイントからCSSを処理する場合は、スタイルシートをモジュールとして直接インポート。
import '@rm-labo/_mq.scss'
使い方
3種類 * 2方向の@mixinsを、キャメルケースかケバブケースで使えるようにした。
任意の箇所でで使うときは以下。各mixinは以下の場面で使う。
-
mq-{'width'|'height'}-up($bp) {}
: スマホファーストの記述用 -
mq-{'width'|'height'}-down($bp) {}
: デスクトップファーストの記述用 -
mq-{'width'|'height'}-between($bp1, $bp2) {}
: 特定範囲用
// CamelCase or kebab-case
.class-name {
// width camelCase
@include mqUp($bp) {}
@include mqDown($bp) {}
@include mqBetween($bp1, $bp2) {}
// width kebab-case (same as camelCase)
@include mq-up($bp) {}
@include mq-down($bp) {}
@include mq-between($bp1, $bp2) {}
// height camelCase
@include mqHeightUp($bp) {}
@include mqHeightDown($bp) {}
@include mqHeightBetween($bp1, $bp2) {}
// height kebab-case (same as camelCase)
@include mq-height-up($bp) {}
@include mq-height-down($bp) {}
@include mq-height-between($bp1, $bp2) {}
}
arguments | type | required | default value | description |
---|---|---|---|---|
$bp |
string , number
|
false |
md |
sm , md , lg , xl , Free px number
|
$bp1 |
string , number
|
true |
- |
sm , md , lg , xl , Free px number
|
$bp2 |
string , number
|
true |
- |
sm , md , lg , xl , Free px number
|
全体で共用するbreakpointが!defaultで定義されている。
// デフォルト値
$mq-breakpoints: (
sm: 600px,
md: 900px, // default
lg: 1200px,
xl: 1800px
) !default;
$mq-breakpoints-default-key: 'md' !default;
プロジェクトに合わせてoverrideして使う。
// override例
$mq-breakpoints: (
micro: 320px,
small: 620px,
medium: 840px, // default
large: 1280px,
extra: 1900px,
// .. 好きなだけ追加 ..
);
$mq-breakpoints-default-key: 'medium';
@import 'path/to/node_modules/@rm-labo/mq-scss/_mq.scss';
基本的にいはスマホファースト且つ、$breakpointsで定義した共用の値のkey sm
md
lg
xl
を引数として受け取るが、その一箇所でしか使わないイレギュラーケース用として、ピクセル値による指定も許容するよう実装する。
また、PC/スマホだけのような1ブレイクポイントの簡易的な実装も多いため、省略してスッキリかけるようにも対応する。
.className {
// SINGLE BREAK POINT
@include mqUp() { /* 900px (default: md) and up */ }
@include mqDown() { /* 900px (default: md) and down */ }
// MOBILE FIRST POLICY
@include mqUp('sm') { /* 600px and up */ }
@include mqUp(900px) { /* 900px and up */ } // イレギュラーな px数でもOK
@include mqUp('lg') { /* 1200px and up */ }
@include mqUp('xl') { /* 1800px and up */ }
// DESKTOP FIRST POLICY
@include mqDown('xl') { /* 1799px and down */ }
@include mqDown(1200px) { /* 1199px and down */ } // イレギュラーな px数でもOK
@include mqDown('md') { /* 899px and down */ }
@include mqDown('sm') { /* 599px and down */ }
// SPECIFY BY RANGE
@include mqBetween('sm', 'md') { /* 600px ~ 899px */ }
@include mqBetween('sm', 'lg') { /* 600px ~ 1199px */ }
@include mqBetween('sm', 1600px) { /* 600px ~ 1599px */ } // 混在OK
@include mqBetween(1234px, 850px) { /* 850px ~ 1233px */ } // 大小逆もOK
}
mq-jsの方針
原則cssで完結するべきだが、どうしてもjsによるサポートが必要なケースもある。
そのため、scssとなるべく近いコンセプトのjsもおまけで作ってみた。
window.matchMedia を使い、ブレイクポイントのkeyの名前とcallbackを受け取るようなラッパーを生成する方針。
インストール
$ npm i -D @rm-labo/mq-js
import Mq from '@rm-labo/mq-js'
使い方
読み込んでインスタンスを作り、それに対して以下のようなメソッドを実行していきます。
当たり前ですがjsなので kebab-case はありません。
import Mq from '@rm-labo/mq-js'
const mq = new Mq()
mq.up( bpKeyName1, matchHandler [, unmatchHandler ])
mq.down( bpKeyName1, matchHandler [, unmatchHandler ])
mq.between( bpKeyName1, bpKeyName2, matchHandler [, unmatchHandler ])
パラメータは以下
arguments | type | required |
---|---|---|
bpKeyName1 | string |
true |
bpKeyName2 | string |
true |
matchHandler | function |
true |
unmatchHandler | function |
false |
// 単一ブレイクポイントの場合
mq.up(
'sm',
() => { /* Fires in 600px and up */ },
() => { /* Fires in 599px and down */ }
)
// 別の明示的な書き方もできるように
mq
.down('md', () => { /* Fires in 899px and down */ })
.up('md', () => { /* Fires in 900px and up */ })
// 特定の範囲だけを対象にしたい場合
mq
.between(
'md',
'lg',
() => { /* Fires in 900 ~ 1199px */ },
() => { /* Fires in ~ 899px and 1200px ~ */ })
// すべての範囲を網羅したい場合はメソッドチェーンでつなげる
mq
.down('sm', () => { /* Fires in 599px and down */ })
.between('sm', 'md', () => { /* Fires in 600 ~ 899px */ })
.between('md', 'lg', () => { /* Fires in 900 ~ 1199px */ })
.between('lg', 'xl', () => { /* Fires in 1200 ~ 1799px */ })
.up('xl', () => { /* Fires in 1800px ~ */ })
scssと同様で高さを対象にできるように。
mq
.heightUp(bpKeyName1, matchHandler [, unmatchHandler ])
.heightDown(bpKeyName1, matchHandler [, unmatchHandler ])
.heightBetween(bpKeyName1, bpKeyName2, matchHandler [, unmatchHandler ])
ブレイクポイントもoprion経由でoverrideできるようにする。
// 例
const option = {
breakpoints: {
'micro' : 320,
'small' : 620,
'medium' : 840,
'large' : 1280,
'extra' : 1900,
// .. 好きなだけ追加 ..
}
}
const mq = new Mq(option)
mq
.down('micro', () => { /* Fires in 319px and down */ },)
.between('micro', 'small', () => { /* Fires in 320 ~ 619px */ },)
.between('small', 'medium', () => { /* Fires in 620 ~ 839px */ },)
.between('medium', 'large', () => { /* Fires in 840 ~ 1279px */ },)
.between('large', 'extra', () => { /* Fires in 1280 ~ 1899px */ },)
.up('extra', () => { /* Fires in 1900px and up */ },)
demo
実際に動かしているページも一応用意した。
もう少しスマートな方法もありそうではあるが、一旦これを使ってみる。
課題
pxベース限定になっている点。
emベースなど別の単位で管理したいケースもあるが今回は実装を見送った。バージョンアップで対応するかもしれない。
(em等、px以外の単位で MediaQuery を書きたい場合に必要なcalc計算が、一部レガシーブラウザ待ちの状況。)