LoginSignup
32
28

More than 3 years have passed since last update.

[React] 年末だしstyled-componentsで爆速レスポンシブ対応していこうな

Last updated at Posted at 2019-12-01

はじめに

こんにちは、ねりこ @nerikosans と申します。もう2019年も終わりが近づいてきましたね。
皆様におかれましてはますます import * as React from 'react'; の由、何よりと存じます。日頃は特別の export default にあずかり心より御礼申し上げます。

さて、Styled Components は、DOMのスタイルを、その定義ファイル( .jsx, .tsx )内で完結させてしまおうという思想のフレームワークです。コードの見通しの良さ、動的レンダリングのしやすさなどから昨今は人気になってい(ると感じてい)ます。

そして、DOMスタイリングにはレスポンシブ対応がつきものですが、やっぱり爆速で書きたいですよね。
ということで、 styled-componentsでレスポンシブするときのメモ書きです。

目標

最低限の記法で書きたいので、これ↓だけ書けば対応が済むように構築します。

const Box = styled.div`
  background: black;

  /* PC */
  ${media.pc`
    background: red;
  `}

  /* Smartphones */
  ${media.sp`
    background: red;
  `}
`;

Step1. styled-media-query

morajabi/styled-media-query
よく使うサイズをサクッと使いたい場合はこれだけで大丈夫。
small, medium, large, huge の4つの width breakpoint を備えていて、media queryを自動で生成してくれます。

公式サンプルが以下の通り。

const Box = styled.div`
  background: black;

  ${media.lessThan("medium")`
    /* screen width is less than 768px (medium) */
    background: red;
  `}

  ${media.between("medium", "large")`
    /* screen width is between 768px (medium) and 1170px (large) */
    background: green;
  `}

  ${media.greaterThan("large")`
    /* screen width is greater than 1170px (large) */
    background: blue;
  `}
`;

便利ですね。でも .lessThan("medium") って毎回書きたくないので、これをwrapします。
styled-media-query の export されていない type を使用しているところがあります。

media.tsx

import media from 'styled-media-query';
import {
  ThemedStyledProps,
  InterpolationValue,
  FlattenInterpolation,
} from 'styled-components';

/**
 * https://github.com/morajabi/styled-media-query/blob/master/src/index.d.ts
 */
type InterpolationFunction<Props, Theme> = (
  props: ThemedStyledProps<Props, Theme>
) => InterpolationValue | FlattenInterpolation<ThemedStyledProps<Props, Theme>>;

type GeneratorFunction<Props, Theme> = (
  strings: TemplateStringsArray,
  ...interpolations: (
    | InterpolationValue
    | InterpolationFunction<Props, Theme>
    | FlattenInterpolation<ThemedStyledProps<Props, Theme>>
  )[]
) => any;

const rules: { [v: string]: GeneratorFunction<unknown, any> } = {
  pc: (...args) => media.greaterThan('medium')(...args),
  sp: (...args) => media.lessThan('medium')(...args),
};

export default rules;

よし、これでこのファイルを media として default importすれば、 ${media.pc` .... `} だけでpc専用スタイルを書けるようになりました!

Step2. カスタマイズ

さて、これだけでも便利ですが、styled-media-query では今のところ pre-defined な4つのサイズ以外は指定できないようなので、これに加えて自由なmedia queryを書きたい場合 (例えば、heightで区切りたい場合) は以下のようにすればOKです。

media.tsx
import media from 'styled-media-query';
import {
  ThemedStyledProps,
  InterpolationValue,
  FlattenInterpolation,
  css,
} from 'styled-components';

/* (... 中略) */

const rules: { [v: string]: GeneratorFunction<unknown, any> } = {
  pc: (...args) => media.greaterThan('medium')(...args),
  sp: (...args) => media.lessThan('medium')(...args),
  short: (...args) => css`
    @media screen and (max-height: 480px) {
      ${css(...args)}
    }
  `,
};

export default rules;

これで新たに media.short`...`が使えるようになりました!

おわりに / 展望

以上で、晴れて簡潔にレスポンシブスタイルが書けるようになりました。最高ですね。

しかし、そもそもComponentを render するかどうかから出し分けたい場合などは、この方法では足りません。
例えば

const media = useMedia();
const isPC = media.pc;

みたいに書けたら便利ですよね。これを実現する方法はまた今度書きたいと思います
追記: 書きました! [React] 年末だしwindow.matchMedia()で爆速レスポンシブ対応していこうな

お読みいただきありがとうございました!

32
28
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
32
28