時間がない人は一番下の「使った感想」までジャンプ!
これはなに
Svelteでemotion(CSSinJS)を試したので備忘録
CSSinJSってなに
CSSをJSで書きますよ〜という概念。
詳しいメリットは後述しますが、ざっくりいえば下記な点より支持されています。
- スコープのないCSSにスコープを付与できる
- 変数・関数がJSとCSSで同じものが使える
主にスタイルにスコープを持てないReactで使われています。
emotion,styled-compornentなど、ライブラリも豊富です。
書いたコードはインラインやCSSファイルとしては吐き出されず、DOMのトップに直接追加されます。

メリットざっくり
- インラインではなく、実際のCSSを生成する
- SCSSより軽いらしい ライブラリ容量:emotion:8.9kb
- JSの変数・関数がそのままCSSに使える(後述)
- CSSにスコープを付与できる
- ただし今回の場合、Svelte自体がスコープを持っているため、特別なメリットではない
デメリットざっくり
- SASSと書き方の異なる部分があり、デザイナーがスタイルを書く場合は学習コストあり
mixinなど、SASS独自の機能は当然使えない- mixinできた
検証したFWについて
emotionを検証しました。
emotionはFWに依存しないCSSinJSのため、公式のブログでもサンプルコードが掲載されています。
(CSSinJSといえばstyled-components一択感はあるが、React依存)
記事
検証
ここからは実際の使い方の説明です。
スタイルの指定
- htmlタグ内ではclassを{}で囲むと適用される
-
<script>
タグ内にconst hoge =
で指定 - importするときは
export const hoge =
にしてね - タグ付きテンプレートリテラルで記述(バッククオーテーションで囲む)
どうやってCSS書くの
CSSinJSだと、文字通りJSでCSSを書くため、
margin-bottom
はmarginBottom
になったり、
ハイフンではなくキャメルケースで書きます。
それがJSXの中でインライン記法のように書けるCSS prop
といいます。
const style = css({
marginBottom: '16px',
borderRadius: '4px',
});
いつものCSSと書き方が異なり書きづらいよ〜という人のために、
タグ付きテンプレートリテラルを使えばいつも通りのCSSの書き方になります。
const list = css `
background: ${Color.BaseColor};
margin-bottom: 16px;
border-radius: 4px;
`
ただし、SvelteではCSS prop
は書けないっぽい (JSXだし?)
コード サンプル
変数はそれだけでまとめるとよさそう
export default {
BaseColor: '#FFF',
KeyColor: '#444',
SubColor: '#ececec'
}
こうやって呼び出す
<script>
import { css } from "emotion";
import Color from "../../apps/style/Color.js";
const list = css `
background: ${Color.BaseColor};
margin-bottom: 16px;
border-radius: 4px;
`
</script>
<li class={list}>
<span class={icon}>
<Label {type} />
</span>
</li>
const list = css ~
でスタイルを定義すると、
templateの方でclass={list}
で適用されます。
この時classはcss-#ハッシュ値
で出力されます。
rollupで消せないかな? できないらしい><

styled-compornent風に、styled.h1 =
のように、
const list = css.li
でタグの指定はできない
<script>
import { css } from "emotion";
export const List = css.li `//タグをこっちで指定
background: ${Color.BaseColor};
margin-bottom: 16px;
border-radius: 4px;
`
</script>
<List>
<Icon>
<Label {type} />
</Icon>
</List>
タグを指定できるメリットとしては、
- 純粋にコンポーネント名だけでかけるのでスッキリする
- h1、h2、spanとpなどがスタイル側で指定できる
があるけど、
前者はtemplate部分だけでhtml構成が判別できないデメリットもあるし、
後者はスタイル側で指定しなくてもいいと思うので、
かけなくても問題はない
Media Queries
//メディアクエリ
export const breakpoints = [480, 768, 980, 1200]//ブレイクポイント
export const mq = breakpoints.map(
bp => `@media (min-width: ${bp}px)`
)
import { mq } from "../../apps/style/Base.js";
//コンテンツ幅の980pxで切り替える
${mq[2]}{
display: block;
}
`
keyframe Animation
//keyframesを指定する
import { css,keyframes } from "emotion";
//アニメーション
//keyframesを先に書かないとエラー
const heightAnimation = keyframes `
from {
opacity:0;
}
to {
opacity:1;
}
`;
const searchBlock = css `
display: none;
//ちゃんとハッシュ値でセットされる
animation: ${heightAnimation} ease-in .3s;
//レスポンシブ
${mq[2]}{
display: block;
}
`
mixin
export function square(val , key) {
return {
width: val,
height: key,
};
}
const regularStyle = css`
${square('24px','12px')}
background: hotpink;
`;
propsを直接スタイルで指定して出し分ける方法
やりたかったこと
- propsでわざわざclassNameを指定しなくてもいいので、スタイルのためにコンポーネントを増やさずに済む
- jQueryでは、onClickでclass名を付与->そのclass名の分だけコンポーネントを追加で作るイメージだった
- propsでprimaryという値を渡せば、1つのコンポーネントの中でスタイルの出しわけができる
- 参考
できたこと
jsの配列をCSSに直接指定
let attr = {
none: {
caption: "",
fontColor: "red"
},
place: {
caption: "ほにゃほにゃ",
fontColor: "orange"
},
image: {
caption: "ふがふが",
fontColor: "blue"
}
};
export const title = css `
color: ${attr[type].fontColor};
font-size: 2rem;
`;
</script>
<ICon {type} />
<label class={title}>{attr[type].caption}</label>
今までは、JSとSASSで変数を二重管理する部分があったので、管理が一元化できてよい
(slickのブレイクポイントとかね)
できないこと
propsでCSSを直接出し分けること。これが本当にやりたかった><
これができると、状態ごとに毎回classを1つ追加する手間が省けて、コード量が減らせる。
例えば、<a class="btn">
というDOMにprimary
という状態を付与する場合、
btnクラスの他にis-primary{foo: bar;};
のclassを新しく付与していた。
CSSに直接propsを渡せる場合、下記のコードのように、
btnクラスの中でprimary
が渡った場合のみ、必要なコードを動的に付与できる。
'@emotion/core'
を入れると使えるけど、React依存のようだ。
<script>
const btn = css `
//backgroundがpropsで渡ってきた色に変更、初期値はgreen
background: ${props => props.background || "green"};
//primaryのpropsが渡ったら、btnにプロパティを追加
${props => props.primary && css`
padding: 1em;
margin: 1em;
color: purple;
`}
</script>
<p>
<a class={btn} {primary}>
test
</a>
</p>
単純に書き方が違うだけかもしれないので、
良い書き方があればアドバイスほしいです!
使った感想
意外と使えそう
公式で言っている通り、SvelteのCSSと併用する前提になってはいるものの
mixin的使い方も可能なので、十分使えそうです。
デザイナーが使う場合、学習期間は設けるとよさそう。
GOOD
- 親コンポーネントと子コンポーネントで同じclass名を指定してもスタイルはあたらない
- mixinもいける
- JS側と変数を共有できるのは嬉しい
- slickのブレイクポイント、カラー変数など、2重管理が防げる
BAD
- emotion公式のGlobal Stylesは使えない
- installの必要な
'@emotion/core'
がreactでしか動かないのが諸悪の根源 - Globalなclassを使う場合、CSSの外部ファイルとしてimportする
- またはSvelteのGrobalとして指定する Using CSS-in-JS with Svelte
- installの必要な
- VSCodeにEmmetがない?
- Svelteがそもそもスコープを持っているので、擬似スコープのうまみはない
デザイナーができそう?できない?
良いか悪いか、SvelteのCSSinJSは特有の複雑さが出てこないので、デザイナーでも慣れればOK
(「できないこと」であげたpropsを直接渡す、はグラフィック寄りのデザイナーはしんどいかも)
検証ポイント早見表
検証項目 | 結果 | 一言 |
---|---|---|
親子コンポーネントで継承できるか | △ | importするかpropsでclassの付け替え |
.svelteファイル、外部ファイルとしても使えるか | ○ | コンポーネントまたいだスタイルは外部ファイルimport |
グローバルで当てられるか | ○ | jsファイルをimport |
レスポンシブ(SP,PC) | ○ | MediaQuery |
keyframe animation | ○ | |
mixin | ○ | |
スタイルに直接propsが渡せるか | △ | 直接は渡せないので、変数・配列としてclass名を渡す、とかになりそう |
参考
emotionを使ったCSS-in-JS
emotion - フレームワークに依存しない洗練された CSS-in-JS
CSS-in-JSのライブラリとして「emotion」を選択している理由