Stylus の変数に JS からアクセスする方法を考える
transition
、animation
が使えるようになり、擬似セレクタの種類も増えてきたことで、スタイル周りの多くのことがCSSだけ(あるいはclassList
のadd
、remove
程度の操作)で書けるようになってきた気がする。
それでも、どうしても JavaScript で操作したい時もまだあって、 Stylus で利用している変数を JavaScript 内でも参照したくなることがある。
いくつか方法を試した結果、結局 Stylus のプラグインを自作することにした。
Atom + pigments
やりたかったことをまとめると、
- Stylus の変数定義ファイルが存在すること
- JS からは
"20px"
のような文字列ではなく、可能な限り20
のように使用しやすい形で取得できること - 実際に変数の定義を書く場所は JS でも Stylus でも良い
- JS 側、 Stylus 側共にコーディングの制約が少ないこと
という感じです。
Stylus の変数定義ファイルが欲しい理由は、
この pigments というプラグインが Stylus の構文をある程度解釈してくれるので、変数にもハイライトが効くようになる。これが大変便利。
(ここでいう変数定義ファイルとはdefine(key, value)
ではなく、key = value
の構文で代入しているコード)
検討した他の手段
調べてみると既にいろんな方法で同じような試みがあった。
rosetta
Stylusだけでなく、Sass、LESSで使うことも出来る。 JS => Stylus でも Stylus => JSでもなく、.rose という拡張子の rosetta ファイルから JS と Stylus(もしくは Sass、LESS)を書き出す仕組みになっている。 最初 README を読んだ時、Stylus で変数定義して rosetta を require すればあら不思議、Stylus の変数が使えちゃう、みたいなノリで書いてあるから勘違いしたがどうやら違う。
それでも rosetta ファイルが Stylus と同じ様に書けるならまあ問題ないかと思い、実際に rosetta ファイルを作ってコンパイルしてみたが、コンパイルエラーになった。
rosetta ファイルは Stylus とシンタックスは同じと書いてあるが、変数名の-
や、タプルの代入などはシンタックスエラーになる。
ということは、つまり Stylus の ファイルをそのまま使えないということになるし、define メソッドなどで定義されたものも共有出来ない。
既存の Stylus が流用できないのは面倒くさそうなので使うのはやめた。
stylus-export-loader
webpack 用ではあるがやりたいことには概ねマッチしている感じがする。 なぜか npm に登録されてないので github リポジトリを package.json に書いてインストールする。
変数定義用の Stylus ファイルを用意しておき、 !stylus-export-loader!
+ そのパスを webpack.ProvidePlugin
経由で読み込むことで任意のネームスペースから Stylus の変数にアクセスできるようになる(と思ったけど結局うまく動かなかったよ。)
stylus-vars, stylus-var
どちらも Stylus の変数を JS で定義出来るよーというもの。 Stylus の変数定義ファイルが生成されないのでこれも見送った。
そこで foovar ですよ
foovar を使うと Stylus の変数を JS ファイルに書き出す事ができる。
Stylus の実行時に変数スコープを調べるので、Stylus 側では制約無く変数を定義して使うことが出来る。
変数定義ファイルも用意出来る。
これでやりたかったことは全部できるようになった。
インストール
$ npm i -D foovar
使い方
CLI の場合
$ stylus -u foovar path/to/file.styl
webpack + stylus-loader の場合
// webpack.config.js module.exports = { stylus: { use: [require('foovar')()] } };
Stylus ファイルの任意の場所で foovar(path)
を呼び出す。
その時点で定義されている変数が JS ファイルにエクスポートされる。
foo = 10px foovar('src/StyleDefinitions.js')
エクスポートされた JS は require
して使うことが出来る。
const StyleDefinitions = require('./src/StyleDefinitions.js'); StyleDefinitions.foo(); // 10 StyleDefinitions.foo.type; // 'px' StyleDefinitions.foo.css; // '10px'
エクスポートできる変数のタイプ
string
、 unit
、 ident
、 rgba
、 hsla
、 cubic-bezier
、 tuple
、 list
、 hash
を書き出すことが出来る。
unit
は px
、 %
、em
、 ms
、 mm
…等の単位付き(あるいは無し)の数値のことで、 varName.type
はその単位を返す(無しの場合は undefined
)。
Stylus:$var-name |
JS:varName() |
JS:varName.type |
JS:varName.css |
---|---|---|---|
'some text' |
'some text' |
'string' |
'some text' |
20px |
20 |
'px' |
'20px' |
50% |
50 |
'%' |
'50%' |
255 |
255 |
undefined |
'255' |
auto |
'auto' |
'ident' |
'auto' |
#112233 |
[17,34,51,1] |
'rgba' |
'#112233' |
#11223344 |
[17,34,51,0.26666666666666666] |
'rgba' |
'#11223344' |
rgba(11,22,33,.4) |
[11,22,33,0.4] |
'rgba' |
'rgba(11,22,33,0.4)' |
hsl(11,22%,33%) |
[11,22,33,1] |
'hsla' |
'hsla(11,22%,33%,1)' |
hsla(11,22%,33%,.4) |
[11,22,33,0.4] |
'hsla' |
'hsla(11,22%,33%,0.4)' |
cubic-bezier(1,0,1,0) |
[1,0,1,0] |
'cubic-bezier' |
'cubic-bezier(1,0,1,0)' |
10px 20px 30px 40px |
[FoovarValue instance x 4] |
'tuple' |
undefined |
1em, 2em, 3em, 4em |
[FoovarValue instance x 4] |
'list' |
undefined |
{ foo: 1em } |
{ foo: FoovarValue instance } |
'hash' |
undefined |
tuple
、 list
、 hash
に関しては、配列・オブジェクトの各要素がそれぞれ type
、 css
を持つ FoovarValue
のインスタンスになっている。
各要素は単項の値と同じように type
、 css
へアクセス出来る。
// foo = 10px 20px 30px 40px // bar = { baz: 1em } const StyleDefinitions = require('./src/StyleDefinitions.js'); StyleDefinitions.foo()[0]() // 10 StyleDefinitions.foo()[1].type // 'px' StyleDefinitions.foo()[2].css // '30px' StyleDefinitions.bar().baz() // 1 StyleDefinitions.bar().baz.type // 'em' StyleDefinitions.bar().baz.css // '1em'
普段よく自分がアクセスしたくなりやすいものを中心に実装した。URL とかはまだ対応していない。
いずれ欲しくなったときに追加するかも知れないししないかもしれない。
Stylus の変数にアクセスしたくなった時は foovar をぜひに。