最初にまとめ: Free-Style とは
CSS in JS の一種
https://github.com/blakeembrey/free-style
CSS in JS の長所はそのままに
- スタイルをローカルスコープで定義可能
- JS の仕組みによる依存性解決 (CommonJS, Require.js, ES modules)
- 使われていないスタイルの自動削除 (UglifyJS 等)
- スタイル-スタイル間、および JS-スタイル間の定数の共有が容易
- JS による柔軟なスタイルの拡張・再利用が可能
CSS in JS が苦手としていた点を改善
- style 属性に指定可能な内容に限らず、 CSS が提供するすべての機能を利用可能
- 疑似クラス
- 疑似要素
- メディアクエリ
前置き: CSS in JS と CSS Modules
前置きの前置き
この記事は、 の emoji でおなじみ (?) の @basarat さんの以下の記事を読んで「へえ」と思って色々調べた結果をまとめたものです。
煽り気味のタイトルも拝借させていただきました。
CSS in JS
- 元ネタ: https://speakerdeck.com/vjeux/react-css-in-js
- 日本語の解説記事: http://qiita.com/koba04/items/0e81a04262e1158dbbe4
- 「ルールセットがグローバルスコープ」、「依存管理が困難」など、 CSS が抱える問題点を解決するものとして提起
使用例
※ http://qiita.com/koba04/items/0e81a04262e1158dbbe4 より引用
var style = {
container: {
backgroundColor: "#ddd",
width: 900
}
}
var Container = React.createClass({
render() {
return <div style={style.container}>{this.props.children}</div>;
}
});
CSS in JS の弱点
- style 属性に指定できないようなスタイルを定義することが不可能、ないし困難 (疑似クラス、疑似要素、メディアクエリ)
- JS でがんばっている (
:hover
を mouse event で実現するなど) ライブラリもある- もっとも有名なのは Radium
- JS で CSS のすべての機能を再現できるわけではなく、限界がある
CSS Modules
- https://github.com/css-modules/css-modules
- CSS ファイルを JS から import して使う
- 実際には、 CSS ファイルからビルド時に CSS と JS が出力され、その JS の方が import される(詳細は使用例を参照)
- 実体は CSS なので、 CSS で実現できることはすべて実現可能
- ルールセットがローカルスコープだったり、スタイルの再利用ができたり、 CSS in JS で実現していたこともある程度は実現可能
使用例
※ https://github.com/css-modules/css-modules/blob/master/docs/pseudo-class-selectors.md より引用
/* component/text.css */
.text {
color: #777;
font-weight: 24px;
}
.text:hover {
color: #f60;
}
上記の CSS ファイルを webpack の css-loader でビルドすると、
._23_aKvs-b8bW2Vg3fwHozO {
color: #777;
font-weight: 24px;
}
._23_aKvs-b8bW2Vg3fwHozO:hover {
color: #f60;
}
みたいな CSS と、
exports.locals = {
text: "_23_aKvs-b8bW2Vg3fwHozO"
}
みたいな JS が出力されるので、
/* component/text.js */
import styles from './text.css';
import React, { Component } from 'react';
export default class Text extends Component {
render() {
return (
<p className={ styles.text }>Text with hover</p>
);
}
};
のように import して使うことができる (class 名の hash 化の仕方は適当です)。
CSS Modules の弱点
- 定数の共有や、スタイルの拡張などは、 CSS in JS のように JS の世界でできたほうがやりやすそう (慣れの問題かもしれない)
- CSS と JS の間での定数の共有ができない
そこで、 Free-Style
- スタイル定義の実体が CSS である点は、 CSS Modules と同じ
- CSS が提供するすべての機能を利用可能
- CSS Modules が CSS を入力として CSS と JS を出力するのに対し、 Free-Style は JS を入力として CSS と JS を出力する
- 開発者が書くのは JS のコードなので、 CSS in JS 的なメリットはすべて享受できる
- もちろん、 CSS に変換される JS コードと、通常の JS のコード間での定数の共有も可能
- React 等特定のライブラリに依存しない
- 例えば Radium は React の使用が前提になっている
- React Free Style という React 向け拡張も用意されていたりはする
使用例
※ https://github.com/blakeembrey/free-style より引用、コメントを一部改変
var FreeStyle = require('free-style')
// Create a container instance.
var Style = FreeStyle.create()
// Register a new, uniquely hashed style.
var STYLE = Style.registerStyle({
backgroundColor: 'red'
}) //=> STYLE には "f14svl5e" が割り当てられ、 Style オブジェクトには ".f14svl5e { background-color: 'red' }" という CSS が登録される
// Inject a `<style />` element into the `<head>`.
Style.inject() // <style>.f14svl5e { background-color: 'red' }</style> が <head> に出力される
// Figure out how to render the class name after registering.
React.render(
<div className={STYLE}>Hello world!</div>,
document.body
)
書き方色々
疑似クラス、疑似要素、メディアクエリ等
var
style1 = style.registerStyle({
position: 'absolute',
top: -42,
'&:before': { // 疑似要素
content: `'»'`,
fontSize: 28,
color: '#d9d9d9',
padding: '0 25px 7px'
},
'&:checked:before': { // 疑似クラス + 疑似要素
color: '#737373'
},
'@media screen and (-webkit-min-device-pixel-ratio:0)': { // メディアクエリ
background: 'none',
top: -56
}
}),
style2 = style.registerStyle({
position: 'relative',
fontSize: 24,
borderBottom: '1px dotted #ccc',
'&.editing': { // 他のクラスとの組み合わせ
borderBottom: 'none',
padding: 0
},
'&:last-child': { // 疑似クラス
borderBottom: 'none'
},
'&.editing .edit': { // 子孫セレクタの指定
display: 'block'
}
});
上記は以下のような CSS に変換されます (class 名の hash 化ロジックは適当)
/* style1 */
.f1n85iiq {
position: absolute;
top: -42px;
}
.f1n85iiq:before {
content: '»';
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
}
.f1n85iiq:checked:before {
color: #737373;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
.f1n85iiq {
background: none;
top: -56px;
}
}
/* style2 */
.fk9tfor {
position: relative;
font-size: 24px;
border-bottom: 1px dotted #ccc;
}
.fk9tfor.editing {
border-bottom: none;
padding: 0;
}
.fk9tfor:last-child {
border-bottom: none;
}
.fk9tfor.editing .edit {
display: block;
}
ポイントは、
- 変換後の class 名は
&
で表現する - 生で書いた class 名 (上記の例では
.editing
,.edit
) は hash 化されずにそのまま使われるため、グローバルな class となってしまう- ただし、
style.registerStyle()
の中でしか使わない (素の CSS には書かない) ことを徹底すれば、 hash 化された (fk9tfor
のような) class 名との組み合わせにより、実質的にローカルスコープにしか影響を及ぼさないと考えることもできる
- ただし、
TodoMVC の実装例
- CSS 部分をすべて Free-Style (React Free Style) で実装した、 TodoMVC アプリケーション
-
git clone
してサクッと動かせるはずなので、よかったら試してみてください
Free-Style の弱点
- 今のところそんなに流行っていないので、乗っかってしまって大丈夫か不安
- CSS Modules との比較でいうと、 CSS Modules は CSS で書くので、「いつでも引き返せる」みたいな安心感は CSS Modules の方が強いと思う
類似ライブラリ
JSS: https://github.com/jsstyles/jss
- Free-Style と同様、 JS を入力として CSS と JS を出力する
- GitHub の star 数的には、 JSS に軍配
- 両者の違いについては Free-Style のこの issue での議論が参考になりそう
- 簡単に言うと、 Free-Style はシンプル、 JSS は多機能
- 自分は上記 issue での Free-Style の @blakeembrey さんの指摘通り、 JSS の機能は過剰に感じたため、 Free-Style の方が好き
感想
実際にコードを書いてみて、 Free-Style なら CSS とうまくやっていけそう、という実感があったので、是非実戦で使ってみたいです。
あとはチームの他のメンバーが気に入ってくれるか次第。