📝

Styledを始めてみたので、なるほどSnippetをメモ

2022/09/30に公開

前提など

動機

ReactやVueを使っていて、スタイリングのみのコンポーネントロジックを持つコンポーネントを、同じ様に管理することに違和感を感じていました。styled-component(今回は@emotion/styled)を使ってみたところ、とても良かったです。棲み分けが自然でとても便利なので、styledの記事などが多い理由がわかりました。
使っていて出てきたstyledでの書き方などを、Snippet的にメモしておこうと思い今回記事にしてみました。

環境

  • React: 18.2.0
  • @emotion/react: 11.10.0
  • @emotion/styled: 11.10.0

ロジックを含むコンポーネントの方ではimport { css } from "@emotion/react";のcssを使っています。その流れで@emotion/styledを使い始めました。

Snippetメモ

基本

よくある見出しなどのh1のスタイリングです。
以前はスタイリングを当てるだけの為に、Reactコンポーネント(***.tsx)を作っていたのですが、この棲み分けはとても良いと感動しています。

typography.ts
export const Heading1 = styled.h1`
  font-size: 2rem;
  letter-spacing: .02em;
`
export const Heading2 = styled.h2`
···

1つのファイルに複数のstyledコンポーネントを書けるのも良い所です。

page.tsx
const Page = () => {
  // ロジックを書く
  return (
    <Heading1>{title}</Heading1>
  )
}

ロジック、インタラクティブなコンポーネントから、スタイリングの情報を可能な限り排除出来たのが、個人的にはとても気に入っています。
styledとReact.FCなどの違いが、vs-code上で色分けされると最高だなと。難しいのかも知れませんが。

Propsについて

propsやコード分割を読みやすく

vs-codeでの使いやすい書き方として、css(emotion)を使っています。
cssを使うとcss構文の保管やハイライトが効き、Propsを使うことでの使いづらさを回避しています。

const StyledComp = styled.div<{ color: string }>`
  ${({color}) => {
    return css`
      color: #ff0000;
    `
  }}
`

cssを使わないと、returnの後のコードが文字列として扱われてしまうので、cssを使ったほうがスピード、可読性ともに良いと思っています。

受け取ったPropsで同じスタイリングを適用したい

同じ見た目、あるいは近い見た目でエレメントだけ変えたい時などの書き方です。
Propsに対応したスタイリングを別で書くだけです。

button.ts
interface P {
  bgcolor?: 'black' | 'gray'
}
const Bgcolor = ({ bgcolor }: P) => css`
  ${bgcolor === 'black' && 'background-color: black;' }
  ${bgcolor === 'gray' && 'background-color: #999;' }
  ${!bgcolor && 'background-color: white;' }
`
const TitleBlock = styled.h1<P>`
  ${Bgcolor}
  font-size: 2rem;
`
const SubTitleBlock = styled.h3<P>`
  ${Bgcolor}
  font-size: 1rem;
  font-weight: normal;
`

共通のスタイリングを使いまわしたい時も同様の方法が使えます。

サードパーティ製コンポーネントの場合(ex:Link)

下記の書き方で警告が出てしまいました。

router.ts
const StyledLink = styled(Link)<{bold?: boolean}>`
  ${ ({ bold }) => bold ? 'font-weight: bold;' : null }
`
警告
Warning: Invalid attribute name: `bold`

ポイントはbooleanの場合に警告が出る所です。<{bold?: 'true'}>の様に文字列を受け取るようにすれば警告は出ません。
ただ、そうすると使用する方でもbold="true"の形でアトリビュートを書く必要があります。調べたところスタイリングする対象に、Propsを渡す方法を変える事が出来きました。Typescriptだと冗長になりますが、使用感としてこちらが気に入っています。

router.tsx
// スタイリング用Propsと対象の型
type P = { bold: boolean } & LinkProps & React.RefAttributes<HTMLAnchorElement>

const StyledLink = styled(({ bold, ...props }: P) => (<Link {...props} />))`
  ${ ({ bold }) => bold ? 'font-weight: bold;' : null }
`

styledが受け取るPropsからstyledで使うものを取り出し、残りをコンポーネントに渡す方法です。これで<StyledLink bold>太字</StyledLink>という書き方でも警告がでなくなりました。
こちらの記事が大変参考になりました。

Propsをエレメントに渡さない

styled-componentの公式(こちら)だと$をprop名の頭につけるとDOM要素などに渡されないと書かれていますが、上記のようなLinkコンポーネントの場合は上手くいきませんでした。Linkが内包するaタグにはPropsの要素が無いという警告が出てしまいました。
HTMLエレメントだと意図通りに動いていました。

const TransientStyle = styled.div<{ $round: string }>`
  border-radius: ${({$round}) => $round}
`

警告が出た時に簡単に対応するのには良いので、こちらの方法が駄目だった時に1つ前の方法を取るのがスムーズそうです。

継承のような事をしたいとき

似たようなスタイリングで一部だけ変えたい時、例えば日本語用と英語用のフォント違いなど。
下記位であればPropsでも良いですが、Propsが増えすぎると逆に使いづらくなるので、コンポーネントとして分けても問題ない場合などに有用かと思います。
また、ベースのPropsもそのまま使えるので、使い所は多いかと思います。

typography.ts
// ベースのスタイリング
const SmallText = styled.span`
  color: red;
  font-size: 0.9rem;
  line-height: 1em;
`
const EngSmallText = styled(SmallText)`
  font-family: 'Figtree', sans-serif;
`

EngSmallTextではSmallTextのスタイリングを継承しつつ、font-familyで追加のスタイリングを行っています。


他にも個人的に知った事、調べたことがあれば追記したいと思っています。

Discussion