Stylusのあまり知られていない機能
というより、自分が最近知った機能。
ハッシュをCSSのプロパティ名と値に展開できる
Stylusではハッシュが使えます。
シンタックスはJSのオブジェクトリテラルとほぼ同じですが、Stylusのnodeをそのまま値に使用できる点が違います。
以下のように、値に10px
と書いてもvalidです。
foo = { width: 10px } // JSと同じようにアクセスできる bar = foo.width bar = foo['width'] // 代入もできる foo.height = 20px foo['height'] = 20px
ハッシュを{foo}
のようにブレースで囲むと、ハッシュのキーバリューがそのままStylusとして解釈され、CSSのプロパティと値に展開されます。
ハッシュがネストになっている部分は、Stylusのネスト(インデント)として解釈されるので、以下のように&
は親セレクタとして展開されます。
foo = { width: 10px, height: 20px, '&:hover': { color: crimson } } .bar {foo} // こう解釈される // .bar // width 10px // height 20px // &:hover // color crimson // => .bar { // width: 10px; // height: 20px; // } // .bar:hover { // color crimson // }
注意点としてネストされたハッシュや、関数が返すハッシュをそのまま展開しようとするとエラーになります。
いったん変数に代入するとうまくいきます。
foo = { bar: { opacity: 1 } } .baz {foo.bar} // エラー .baz {foo['bar']} // エラー qux = foo.bar .baz {qux} // => .baz { // opacity: 1; // }
JSONを読み込んで色々できる
json
というビルトイン関数を使用して、JSONを読み込むことができます。
キーバリューがそのまま変数名と値として展開され、変数が定義されます。
ネストされている場合は、各階層のキーが-
で連結された物が変数名になります。
{ "foo": "10px", "bar": { "baz": "20px", "qux": "'Helvetica Neue'" } }
上のJSONを読み込むと以下のように変数定義が行われます。
注意点として、ダブルクォートはトリムされるので文字列を値にしたい場合は内側でさらにシングルクォートで囲うなどする必要があります。
json('./vars.json') // foo = 10px // bar-baz = 20px // bar-qux = 'Helvetica Neue' .title font-family bar-qux // => .title { // font-family: 'Helvetica Neue'; // }
json
関数には以下のオプションが用意されています。
hash
leave-strings
optional
hash
JSONをそのままハッシュとして読み込みます。
vars = json('./vars.json', { hash: true }) p(vars.foo) // => inspect: 10px p(vars.bar.qux) // => inspect: 'Helvetica Neue'
leave-strings
ダブルクォートをトリムしないようにする、読み込まれる値が全て文字列になります。
leave-strings
は実際にはhash
オプション前提のようで、hash
をつけなくてもハッシュとして読み込まれます。
vars = json('./vars.json', { hash: true, leave-strings: true }) p(vars.foo) // => inspect: '10px' p(vars.bar.qux) // => inspect: ''Helvetica Neue''
optional
jsonファイルが存在しなくてもエラーにならない。
こちらもhash
オプション前提で、hash
をつけなくてもハッシュとして読み込まれます。
vars = json('./nothing.json', { hash: true, optional: true }) p(vars) // => inspect: null
JavaScriptの関数を呼べる
こうしてStylusの中でハッシュも配列も関数もあって、文字列操作もそれなりに用意されていると、だんだん複雑な関数を作り始めたりします。
とはいえ、JSで書けたら楽なのになーという場面も結構あります。
そんな時にもStylusにはJavaScriptを呼び出すためのAPIが用意されています。
JavaScript側では以下のようにプラグインを作っておき、Stylus側ではuse
関数を使って読み込みます。
// add.js module.exports = () => stylus => { stylus.define('add', (a, b) => a.operate('+', b)); };
use('path/to/add.js') res = add(10px, 20px) // => 30px
CLIの場合でも-u
オプションで同じように使用できます。
$ stylus -u ./path/to/add.js src/index.styl -o dist
Stylusのノードを作って返すようなこともできるみたいです。
この辺ドキュメントがあまり詳しく書かれてないのでよく分かってないです、またいずれ調べてみます。
nibはおそらくこんな感じでJavaScriptのAPIからnode-canvasを呼び出してgradientをpng画像に変換したりしてるんだと思います。
他にも公式ページを見ていると色々知らない機能が出てくるので面白いです。
button要素にはflexboxを使わないほうが良さそうです
<button>
要素にflexboxを適用したところ、Chrome、Safari、FireFoxでそれぞれ表示が違った。
IEではまだ試していない。
調べてみるとどうやらこういう事らしい。
stackoverflow: Flexbox not working on <button>
element in some browsers
The problem is that the
<button>
element cannot be a flex container (in some browsers).Certain HTML elements, by design, do not accept display changes. Three of these elements are:
<button>
<fieldset>
<legend>
The idea is to maintain a level of common sense when styling HTML elements. For instance, the rule prevents authors from turning a fieldset into a table.
button
要素はflex containerにはなれないと。
上記三つの要素はCertain HTML elements
(用途が明らかな要素?)なので、display
の値は変えられないよ、というような事が書いてあるような気がします。
デモ
Chromeだと他の通常のブロック要素と同じようにflexboxが適用される。
Safariだとdisplay: flex
、flex-flow: row nowrap
は効いてるっぽいが、justify-content
が効かない。
FireFoxだとそもそもdisplay: flex
が効かない。
// jade button i.material-icons looks_one i.material-icons looks_two .button i.material-icons looks_one i.material-icons looks_two
// stylus @import 'nib' button, .button size 200px 50px padding 10px margin 20px text-align center box-sizing border-box display flex // FireFoxで効かない flex-flow row nowrap justify-content space-around // Safariで効かない outline none border none background-color #3399aa .material-icons size 30px auto line-height 30px background-color #bbb
各ブラウザのスクリーンショットこんな感じです。
chokidarを使ってファイルの変更をwatchする
Riot.jsの話。
カスタムタグが増えるにつれ、Riot CLIによるコンパイル時間がバカにならなくなってきた。
特にwatchの立ち上げ時に時間がかかる。
コンパイルオプションとしてBabelとStylusを使用しているので、最初これらを疑ったが、buildのみだとさほどかからないので、Riotのwatchオプション内で何かまずい事が起きていると思われる。
そこで、私の上長がファイル変更の監視をchokidarで行う方法に書き直したところかなり改善された。
ソース全体のビルドはnpm run build
のみで行い、npm run watch
では初回ビルドを行わないようにした。
{ "scripts": { "build": "riot --type babel src/tags lib/tags", "watch": "SHELL=/bin/bash chokidar 'src/tags/**/*.tag' -c 'OUTPATH=`echo {path} | sed -e \"s/^src/lib/\" -e \"s/\\.tag$/.js/\"` && mkdir -p $(dirname $OUTPATH) && riot --type babel {path} $OUTPATH'" } }
こういう時、sedの正規表現が分からなくて、大体いつもここを見に行っている。
chokidar
chokidarは指定した対象の変更をフックして、任意の処理を実行する事ができるnpm package。
みんなつこてる。
It is used in brunch, gulp, karma, PM2, browserify, webpack, BrowserSync, socketstream, derby, and many others. It has proven itself in production environments.
使い方
-c
または--command
に続けてフックさせたい処理を渡す。
$ chokidar '監視したい対象をglobで指定' -c '実行したいコマンドを渡す; この中では{event}で変更の種類、{path}で変更のあったファイルのパスが参照できる'