0
0

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.

mediaQueryの記述を効率化するためのラッパーを npm package で作ってみた

Posted at

はじめに

毎回微妙に違うけど似たようなロジックを、プロジェクト毎に組んでしまいがちです。
どうせいつも使うなら、自分用に 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計算が、一部レガシーブラウザ待ちの状況。)

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?