Masayan tech blog.

  1. ブログ記事一覧>
  2. 【CSS in JS】Reactでstyled-compornentを使用して、クリーンなコンポーネント設計を行う

【CSS in JS】Reactでstyled-compornentを使用して、クリーンなコンポーネント設計を行う

公開日

Css in Js is 何

  • CSS in JSは、JavaScriptの中にCSSの記述を取り込んでしまう手法
  • styled-compornent,glamorous,Emotion,Linariaなどのライブラリが存在
  • 最も有名なのが、今回ご紹介するstyled-compornent

事前準備

インストール

npm install --save styled-components

// with Ts
npm install @types/styled-components

// or

yarn add styled-components

任意のjsxファイル内でimport

import styled from 'styled-components';

基本的な特徴

  • CSSで表現できるおおよその内容はstyled-componentsでも実現可能
  • スタイルごとにclass名を考えるコストが無くなる
  • 最終的に、styled componentsが生成するclassセレクタは、ユニークな文字列でできている。
    (例.sc-hogehoge)
    ※classNameをpropsでわたせば、任意のclass名を付与することは可能
  • vendor prefixは自動付与されるので、-webkit-・・のようなautoprefixerは不要
  • スタイルをあてることが可能なHTML要素であれば、styled componentsでコンポーネントとして作成可能
  • CSSプリプロセッサとしてstylisが組み込まれているため、SassのSCSS記法のような書き方も可能(ネストやベンダープレフィックス)
  • サポートするブラウザや対象とするバージョンの範囲を任意に指定することは不可。Reactのバージョンに応じたブラウザサポートに準拠する

最も基本的な書き方

大きく2パターンありますが、直感的にわかりやすいのは前者かと思います。

タグ付きテンプレートリテラルで記述する

  • CSSのsyntax(構文)がそのまま使える
  • styledを用い、タグ付きテンプレートリテラルを受け取ってstyled componentを返す関数を生成する
  • styled.要素名`CSS` のように指定する
  • ケバブケース(文字区切りにハイフンを使用)(font-size
  • 末尾はセミコロン
import React from 'react'
import styled from 'styled-components'

const Button = styled.button`
    height: 100%;
    color: #fff;
    font-size: 12px;
    ...
`
const BasicButton = (props) => {
    return (
        <Button>
            {props.buttonName}
        </Button>
    )
}

export default BasicButton

オブジェクトリテラルで記述する

  • styled.span({ })のように、オブジェクト形式で記述する
  • キャメルケース(fontSize
  • 末尾はカンマ
  • CSSファイルからstyled-componentsにコピー&ペースト(あるいはその逆)できない ←重要
import React from 'react'
import styled from 'styled-components'

const Button = styled.button({
    fontSize: '12px',
    height: '100%',
    color: '#fff'
})

const BasicButton = (props) => {
    return (
        <Button bg={props.bg} className={props.className} onClick={props.onClick}>
            {props.buttonName}
        </Button>
    )
}

export default BasicButton

応用的な書き方

&でネストする

Scssのようなネスト記法(使いすぎるとコンポーネント指向の設計が無意味になりかねないので注意.)

const Container = styled.div`
  width: 100vw;
  height: 100vh;
  div{
    width: 40%;
    h1{
    color: #646464;
  }
  p{
   color: #646464;
  }

疑似クラス

hoverのような疑似クラス

※疑似要素と疑似クラスの違い

擬似要素は文章の中の1行・1文字単位の変更を加えることや、要素に文章や画像などを付け加えるために使用されますが、擬似クラスは、マウスホバーなどの文章構造の範囲外にある情報や、偶数のpタグにだけスタイルを指定するなどの、単体のセレクタでは表現できないものを選択をするために使用されます。

const Button = styled.button`
    ...
    &:hover {
        text-decoration: none;
        opacity: 0.7;
        box-shadow: 0 0 10px rgba(0 0 0 / 0);
    }
   ...
`

疑似要素

after,beforeなどのような疑似要素

const ... `
::after{
    content: '';
    ...
}
`

動的なスタイルの切り替え

propsによる動的なスタイルの切り替え

テンプレートリテラル

import React from 'react'
import styled from 'styled-components'

const bgList = {
    BLUE: '#3283BB',
    GREEN: '#46A6AE',
    PURPLE: '#C129D1',
    YELLOW: '#FDB515'
}

const Button = styled.button`
    color: #fff;
    font-size: 12px;
    background: ${(props) => bgList[props.bg]}; // 動的な背景色の切り替え
`

const BasicButton = (props) => {
    return (
        <Button bg={props.bg}>
            {props.buttonName}
        </Button>
    )
}

export default BasicButton

オブジェクトリテラル

const Button = styled.button({
    fontSize: '12px',
    height: '100%',
    background: (props) => props.bg // 動的な背景色の切り替え
})

const BasicButton = ...

data属性

もちろん、htmlのdata属性にスタイルを当てることも可能

&[data-color='black']{ 
  background: ${Color.black};
}

メディアクエリ

  1. デバイスごとのブレークポイントを定義
    const Size = {
        mobileS: '320px',
        tablet: '768px',
        laptop: '1024px',
        desktop: '2560px'
        ...
    }
    
    export default Size
  2. 定義したデバイスごとのブレークポイントに基づいて、デバイスごとにメディアクエリを作成
    import Size from './Size'
    
    const Device = {
        mobileS: `(min-width: ${Size.mobileS})`,
        mobileM: `(min-width: ${Size.mobileM})`,
        ...
        tablet: `(min-width: ${Size.tablet})`,
        laptop: `(min-width: ${Size.laptop})`,
        desktop: `(min-width: ${Size.desktop})`,
    }
    
    export default Device
  3. 対象のコンポーネントにデバイスごとのスタイルを定義する
    例えば、子要素をラップするコンポーネントであれば、max-widthでデバイスに基づいて異なるものを指定するだけでOK
    // Wrapper.jsx
    
    import React from 'react'
    import styled from 'styled-components'
    import Device from '../../../shared/style/Responsive/Device'
    
    const Wrapper = styled.div`
        margin: auto;
        @media ${Device.laptop} {
            max-width: 800px;
        }
    
        @media ${Device.desktop} {
            max-width: 1400px;
        }
    `
    const BasicWrapper = ({ children }) => {
        return <Wrapper>{children}</Wrapper>
    }
    
    export default BasicWrapper

@keyframes

アニメーションの@keyframes

import styled, { keyframes } from 'styled-components';

const logoRotate = keyframes`
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
`;

const LogoImage = styled.img`
  animation: ${logoRotate} infinite 10s linear;
`;

...
<LogoImage src="ファイルパス" alt="" />

スタイルコンポーネントの拡張

  • 作成したスタイルコンポーネントはextendで拡張できる ←重要
  • StyledComponentクラスでない場合(styled.で作成したコンポーネント出ない場合)は、記述方法が異なるので注意(styled(コンポーネント)のように記述する)
const Button = styled.button` //ベースとなるボタン
  padding: 1em;
  font-size: 14px;
`

const ExtendedButtonSc = Button.extend` //ベースとなるボタンを拡張
  background-color: #4c6ef5;
  color: #fff;
`

const ExtendedButtonFc = styled(Button)` // StyledComponentクラスでない場合の拡張
  background-color: #4c6ef5;
  color: #fff;
`

スタイルコンポーネントの拡張のその先

withComponent

withComponentを使えば、styled-componentsでstyleがあてられたコンポーネントを、そのコンポーネントとは異なるタグで拡張できる

// button要素にあてたスタイルをaタグにリンクとしてスタイルを適用できる
const LinkItem = Button.withComponent('a')

ThemeProvider

  • コンポーネントに渡すスタイルが複数種類ある場合、themeプロップスにより外から渡すことが可能装飾などに関する規定値を提供してくれる
  • theme propsを通して<ThemeProvider />配下のstyled componentにテーマのスタイルをあてることが可能
  • 色やレイアウトなどを共通化するための仕組み

まず、Themeを作成する

Theme.js

const Theme = {
    colors: {
        text: '#2c3e50',
        background: '#fff'
    },
    fontSize: '14px'
}

export default Theme

ThemeProviderコンポーネントでラップし、themeプロップスを渡す

import { ThemeProvider } from 'styled-components'
import { Theme } from './Theme'

<ThemeProvider theme={Theme}>
  <SomethingComponent1 />
  <SomethingComponent2 />
</ThemeProvider>

ラップされたコンポーネント内では、propsで受け取ることが可能なので、これをもとにstyled componentを生成する

const SomethingContainer = styled.div`
  color: solid 1px ${(props) => props.theme.colors.text};
  background-color: solid 1px ${(props) => props.theme.colors.background};
  font-size:props.theme.fontSize;
`

...

以上です。

まとめ

いかがでしたでしょうか。現代のモダンフロントエンド開発においては、コンポーネント設計がもはや必須の技術となっています。そこで今回は、クリーンなコンポーネント設計を行うために必要なCss in JsについてReactベースで紹介しています。具体的には、最も人気のあるCss in Js用のライブラリであるstyled-compornentの基本的な使い方や一歩踏み込んだ応用的な使い方も説明していますので是非参考にしてみてください。