styled-components vs SASS (SCSS)

## 概要

Reactと一緒にCSSを使うときstyled-componentsとSCSSがメジャーな選択肢だと思いますが、両方使った経験からどちらがいいか紹介したいと思います。結論から言うと、シンプルでメンテしやすいstyled-componentsが個人的に好みです。

## 比較

### スタイルは別ファイルにすべきか

とりあえず同じファイルに書いてしまうのがいいと思います。自然に書けば、styled-componentsはJSと同じファイル、SCSSは別ファイルにスタイルを書くことになります。初めはコンポーネントのロジックとスタイルは全くの別物で、スタイルは簡単に入れ替えできるようにすべきと思っていたのですが、ReactでSCSS使っていく内に考えが変わってきました。別ファイル論は理想だとは思いますが、実際に複雑なコンポーネントを作ってみるとそのHTML・JSとCSSは密接に関係していることが分かります。言い換えれば、HTMLやJSを編集したときにCSS側にも変更が加わる場合が実際には多いということです。Vue.jsでもデフォルトでそういった考えが採用されていて、HTML・CSS・JSを一つのファイルに書くようになっています。

下のTypeScript・SCSSコードはアイコン付きのボタンの例([コード元](https://github.com/raviqqe/tasks/blob/master/src/component/IconedButton.tsx))です。`IconedButton`は受け取ったアイコンとテキストを使ってボタンを作るコンポーネントですが、SCSSの方に謎の`margin-right: 0.2em;`が入っています。これはアイコンに使用している`react-icons`モジュールのために入れています。`react-icons`に入っているアイコンはどれも薄いスペースで囲まれているため、テキストの方に少し`right-margin`を入れないと、ボタンとテキスト全体が微妙に右寄りに見えていまいます。

```jsx:IconedButton.tsx
import * as React from "react";

import Button from "./Button";

import "./style/IconedButton.css";

interface IProps {
    className?: string;
    icon: JSX.Element;
    onClick?: () => void;
    type?: string;
}

export default class extends React.Component<IProps> {
    public render() {
        const { children, className, icon, onClick, type } = this.props;

        return (
            <Button
                className={className || "IconedButton"}
                onClick={onClick}
                type={type}
            >
                <div className="icon">{icon}</div>
                <div className="label">{children}</div>
            </Button>
        );
    }
}
```

```scss:style/IconedButton.scss
@import "../../style/colors.scss";
@import "./Button.scss";

@mixin IconedButton($background-color: $red) {
        @include Button($background-color);

        > .icon {
                font-size: 1.5em;
                line-height: 0;
                margin-right: 0.1em;
        }

        > .label {
                margin-right: 0.2em; // <- ここ
        }
}

.IconedButton {
        @include IconedButton;
}
```

### 再利用のしやすさ

コンポーネント系のライブラリを使わずにHTMLをベタ書きあるいはMarkdown等で簡単にWebサイトを構築する場合でも、SCSSでスタイルを書いておけば他のプロジェクトの資産が使えます。ただし前述したように、その場合はロジックとスタイルをきっちり分けて書く必要があります。[DOMでそのまま使うライブラリ](https://github.com/styled-components/styled-elements)もあるにはありますが、styled-componentsは現在のところReactやVueあたりのメジャーなライブラリのみをサポートしているようです。また、内部でSCSSの資産がある場合や[Bootstrap](https://getbootstrap.com/)等の既存のライブラリを使いたい場合もstyled-componentsのメリットは減ると思います。

### CSS+αとして

SCSSはCSSと比べてかなりリッチで、Mixin・関数定義・色の加工など非常に多くの機能を持っています。styled-componentsで追加されている機能としては`&`による参照と継承くらいで元のCSSと使用感はそれほど変わりません。

## まとめ

styled-componentsはシンプルでJSと同じファイルでスタイルを管理できます。SCSSは多機能で再利用もしやすいですが、その分使いやすさやメンテしやすさで劣ります。基本的に新しいプロジェクトの場合はstyled-componentsがオススメですが、内部あるいはサードパーティのSCSSライブラリを利用したい場合はSCSSを検討してみてもいいかもしれません。

## 他の選択肢

- [CSS Modules](https://github.com/css-modules/css-modules): [create-react-app](https://github.com/facebook/create-react-app/blob/fffe909731ee65a2ce34b317fe91febfc7f80819/packages/react-scripts/template/README.md)がデフォルトでサポート。少なくとも周りではSCSS派が多くあまり聞かない。
- [CSS in JS (JSS)](https://github.com/cssinjs/jss): JavaScriptのオブジェクトの構文を使ってCSSを書くモジュール。React Nativeを使うとデフォルトでこんな感じのことをさせられる。