Skip to main content

5.Styled components and system

styled-components とは

React は HTLM + CSS とは違った方法で DOM にスタイルを当てる。 標準の React を使ったスタイルの適応は、スタイルを定義した JSON を、スタイルを適応したい VirtualDOM に読み込ませることで反映させる。 この方法は CSS in JS と呼ばれている。React 以外のライブラリを使わなくても良いので、ちょっとしたデザインを当てたい時は便利である。

一方で、この方式は大規模な web サービスやレスポンシブデザインを利用する場合に、あまりに多くの記述が必要となり現実的ではなくなる。

styled-component は React で利用する VirtualDOM にあらかじめ css ライクなスタイルを当てておことができるライブラリである。 例えば、web サイト上で表示する標準的なテキストのスタイルを以下のように定義しておき。

const Text = styled.p`
font-size: 12px;
font-color: #003300;
line-height: 16px;
letter-spacing: 1.1;
`

VirtualDOM 上ではこのように使うことでスタイルを使い回すことができる。

render(
<div>
<Text>Hello</Text>
</div>,
)

CSS in JS のような記述にも対応しており、フォントカラーをレッドにしたい場合はこの様に上書きができる。

render(
<div>
<Text style={{ color: 'red' }}>Hello</Text>
</div>,
)

styled-components はそのほかにもテーマ化したり、VDOM から引数を受け取り動的に css の値を変更できたりする。

基本的な利用方法については公式サイトの Basic に書かれている。

https://styled-components.com/docs/basics

styld-system とは

styled-system とは styled-components を拡張して、より動的にスタイル変更を VDOM から可能にするライブラリである。 例えば、styled-components から font-size を動的に変えるには下の 2 通りの方法が一般的である。

上で示したように css-in-js で直接書き換える方法。

<Text style={{ fontSize: '15px' }}>Hello</Text>

または Text コンポーネントをこの様に変更して、使うこともできなくない。

const Text = styled.p`
font-size: ${(props) => props.fontSize || 12}px;
font-color: #003300;
line-height: 16px;
letter-spacing: 1.1;
`

<Text fontSize={15}>Hello</Text>

styled-system も上の様な記述に近くはなるが、レスポンシブに対応しており、より記述も簡単である。 styled-system が提供する typography を渡すことで、fontSize の動的変更が可能になる。デフォルト値は 12px になる。

const Text = styled.p`
font-size: 12px;
font-color: #003300;
line-height: 16px;
letter-spacing: 1.1;
${typography}
`
// smでは10, mdでは11, lgでは12を指定できる
<Text fontSize={[10, 11, 12]}>Hello</Text>

styled-system のドキュメントはこちら

https://styled-system.com/getting-started

インストール

styled-components と styles-sytem をインストールする。

yarn add styled-components styled-system 

設定

next.config.jsを開いて、styled-componentsのcompilerを加える。

/** @type {import('next').NextConfig} */
const nextConfig = {
compiler: {
styledComponents: true,
},
}

export default nextConfig

テーミング

styled-components にはテーマの定義が可能であり、このテーマは styled-system でも利用できる。

このテーマを利用する目的は簡単にスタイルを使い回すためである。 material-ui では色はprimarysecondaryで指定して、それを使い回す。 同じように、テーマで border のスタイルを1px solid #33ab8fと宣言しておけば、インデックス番号から簡単に利用することができる。

ただ、このやり方は動的に引数を VDOM に渡しスタイルを当てる styled-system とは相性が悪いため利用する場面は少ない。 雄一レスポンシブのブレークスポイントの定義でこのテーマを利用する。

まずは、libフォルダを作成し、theme.jsファイルを作成する。

mkdir lib
touch lib/theme.js

中身をこの様にする。

lib/theme.js
const breakpoints = ['560px', '960px']
breakpoints.md = breakpoints[0]
breakpoints.lg = breakpoints[1]

export const theme = {
breakpoints,
mediaQueries: {
nonMobile: '@media not all and (hover: none)',
md: `@media screen and (min-width: ${breakpoints.md})`,
lg: `@media screen and (min-width: ${breakpoints.lg})`,
},
space: [],
fontSizes: [],
fontWeights: [],
lineHeights: {
solid: 1,
title: 1.25,
copy: 1.5,
},
colors: {},
letterSpacings: {},
fonts: {
serif: 'athelas, georgia, times, serif',
sansSerif:
'-apple-system, BlinkMacSystemFont, "avenir next", avenir, "helvetica neue", helvetica, ubuntu, roboto, noto, "segoe ui", arial, sans-serif',
},
borders: [],
radii: [],
width: [],
heights: [],
maxWidths: [],
}

上の記述は基本的には styled-components の theme をリセットしている。

fontsは利用する font-family をデフォルトで定義している。 breakspointssm < 640 < md < 820 < lg、左からスマホ、タブレット、PC の3つの区分を定義している。プロジェクトに応じて分割単位を変えると良い。

なお、breakspointsを変数で切り出し、mdlgのエイリアスを作成し、mediaQueriesで利用しているのは公式サイトのここから参照している。

https://styled-system.com/theme-specification/#breakpoints

mediaQueriesは Styled-component で作られるコンポーネントの中で固有のレスポンシブデザインを反映するときに利用する。 nonMobileはちょっと特殊な css の指定で、スマホ、タブレット等のタップできる端末ではないことを判定している。hover みたいにカーソルがある場合に利用したい css はこの nonMobile で囲み利用する。

Next.jsでは、すべてのスタイルを収集し、<head>タグに適用するグローバルスタイルレジストリコンポーネントの実装を提案している。

https://nextjs.org/docs/app/building-your-application/styling/css-in-js#styled-components

をラップするコードを作成する。 lib/registry.jsファイルを用意して以下のコードを追加する。

この記事を参考にして進めた。

https://dev.to/rashidshamloo/using-styled-components-with-nextjs-v13-typescript-2l6m

lib/registry.js
'use client'
import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

export default function StyledComponentsRegistry({ children }) {
// Only create stylesheet once with lazy initial state
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet())

useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement()
styledComponentsStyleSheet.instance.clearTag()
return <>{styles}</>
})

if (typeof window !== 'undefined') return <>{children}</>

return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance} disableVendorPrefixes={false}>
{children}
</StyleSheetManager>
)
}

Providerを作成する。これはクライアント側で利用する諸々のスタイルを定義する。 ついでに、destyleもこちらに持ってくる。

app/Providers.jsを作成し、以下を追加する。

app/Providers.js
'use client'
import StyledComponentsRegistry from '../lib/registry'
import { ThemeProvider } from 'styled-components'
import { theme } from '../lib/theme.js'
import 'destyle.css'

const Providers = (props) => {
return (
<StyledComponentsRegistry>
<ThemeProvider theme={theme}>{props.children}</ThemeProvider>
</StyledComponentsRegistry>
)
}

export default Providers

スタイルをNext.jsのページ全体で共通化するために、app/layout.jsを開きProvidersを追加する。

app/layout.js
import Providers from './Providers'

export default function RootLayout(props) {
return (
<html lang="ja">
<body>
<Providers>{props.children}</Providers>
</body>
</html>
)
}

以上で styled-components と styled-system の設定が完了した。

git add .
git commit -m "Setup styled-components and styled-system"