• ..

Web Service

    要素を作る

    styled

    styled-componentsからstyled(default)をインポートして使います。styled[htmlタグ名]にはそれぞれタグ付けテンプレート構文になっているのでここに CSS を書いていくだけでスタイル付けした要素を作ることができます。実際には

    1. 定義した CSS にstyled-componentsが適当なクラス名を振る
    2. それを実際に使っている要素のclassNameに指定する

    といった感じのことをやっています。

    以下はスタイル付けdiv要素を作っている例です。

    import styled from 'styled-components';
    
    const StyledDiv = styled.div`
      padding: 1em;
    `;
    
    <StyledDiv>foo</StyledDiv>

    作られた要素はそのまま React コンポーネントとして使うことができます。

    動的スタイル・動的属性

    普通の React コンポーネントがpropsを渡せるようにstyled-componentsで作った要素にもpropsを扱える仕組みがあります。それにはタグ付けテンプレート構文の中に関数を埋め込みます。関数を埋め込むとそれにはpropsが渡ってくるのでそれを好きなように触って目的の値を返すようにします。

    const MARGIN = '1em';
    
    const StyledDiv = styled.div`
      padding: ${props => props.padding || 0};
      margin: ${MARGIN};
    `;
    
    <StyledDiv padding="1em">foo</StyledDiv>
    // padding: 1em;
    // margin: 1em;

    ちなみに埋め込みには関数以外を指定するとそのまま出力されます。

    属性の場合は、attrsというメソッドがあるのでこれでpropsを受け取るコールバックを定義します。以下はtitle属性値を少し編集してる例ですが、このように好きな属性値のオブジェクトを返すようにするといいです。

    const StyledDiv = styled.div.attrs(props => {
      return {
        title: `edited ${props.title}`
      }
    })`
      // ...
    `;

    共通スタイル

    styled-componentsが提供しているcssヘルパーを使うことで共通部分を分けることができます。特にpropsを扱いたいときの共通部分の切り出しには必須です。

    import styled, {css} from 'styled-components';
    
    const common = css`
      padding: ${props => props.padding || 0};
    `;
    
    const StyledFooDiv = styled.div`
      ${common};
      margin: 1em;
    `;
    
    const StyledBarDiv = styled.div`
      ${common};
      margin: 2em;
    `;

    テーマ

    親階層でThemeProviderコンポーネントを使うことでそれ以下のすべてのstyled要素のprops.themeに値を渡せます。全体を通して一貫した動的な値を渡すことができます。

    import styled, {ThemeProvider} from 'styled-components';
    
    const StyledDiv = styled.div`
      padding: ${props => props.theme.padding || 0};
    `;
    
    const App = () => (
      <StyledDiv />
    );
    
    const Root = () => (
      <ThemeProvider theme={{padding: '1em}}>
        <App />
      </ThemeProvider>
    );
    
    <Root />

    メディアクエリのヘルパーを作る

    cssをラップするとメディアクエリが楽に書けるようになります。

    css

    この関数は、styled[element]テンプレートリテラルの中でさらに同じような書き方で書いたスタイルを埋め込みたい時につかいます。共通部分のスタイルを外部にまとめたいときとか便利なやつです。

    import styled, {css} from 'styled-components';
    
    const margin = css`
      ${props => props['data-margin'] || '10px'};
    `;
    
    const Div = styled.div`
      margin: ${margin};
    `;
    Edit styled-components の css

    ラップしよう

    テンプレートリテラル関数

    以下はテンプレートリテラル関数の例です。戻り値はcssの戻り値と同じです。なので${desktop...}...はスタイル)という感じで使うことができます。しかもそのスタイルはメディアクエリで囲まれた状態になります。

    const desktop = (first, ...interpolations) => css`
      @media (max-width: ${sizes[label]}px) {
        ${css(first, ...interpolations)}
      }
    `;

    まとめて定義

    以下はオブジェクトの情報をまとめて、反復処理することでまとめて定義しています。これはmedia.desktopmedia.tabletといった形で使うことができます。

    import {css} from 'styled-components';
    
    const sizes = {
      desktop: 992,
      tablet: 768,
      phone: 576,
    };
    
    const media = Object.keys(sizes).reduce(
      (acc, label) => {
        acc[label] = (first, ...interpolations) => css`
          @media (max-width: ${sizes[label]}px) {
            ${css(first, ...interpolations)}
          }
        `;
    
        return acc;
      },
      {},
    );

    TypeScript

    ちなみに TypeScript で書こうとするとこのようになります。

    import {css, CSSObject, SimpleInterpolation} from 'styled-components';
    
    const sizes: {[index: string]: number} = {
      desktop: 992,
      tablet: 768,
      phone: 576,
    };
    
    const media = Object.keys(sizes).reduce(
      (acc, label) => {
        acc[label] = (
          first: TemplateStringsArray | CSSObject,
          ...interpolations: SimpleInterpolation[]
        ) => css`
          @media (max-width: ${sizes[label]}px) {
            ${css(first, ...interpolations)}
          }
        `;
    
        return acc;
      },
      {} as {[index: string]: Function},
    );
    
    Edit メディアクエリのヘルパー

    <body> や <html> などラップできない領域のタグのスタイルを書く

    createGlobalStyleを使います。これはタグテンプレートになっていて、 CSS を書いて実行するとGlobalStyle(名前はなんでもいい)をコンポーネントを返します。これはレンダリング時にタグテンプレートに渡した CSS を<head>領域に追加してくれます。

    createGlobalStylestyled-componentsから取り込めます。

    import {createGlobalStyle} from 'styled-components';

    あとはstyled.divのあとのようにスタイルを書くだけです。コンポーネントの中(children)には何も入れてはいけません。

    const GlobalStyle = createGlobalStyle`
      html,
      body {
        height: 100%;
      }
    `;
    
    () => (
      <>
        <GlobalStyle />
        <ReduxProvider store={store}>
          <App />
        </ReduxProvider>
      </>
    );