Hugo のテーマを作成したときに、少しだけ CSS を記述しました。 SCSS にしようか、 LESS にしようか、とも思いましたが、 Stylus を使うことにしました。
環境
- Stylus 0.54.5
install
Installing Stylus is very easy once you have Node.js. So get the binaries for your platform and make sure that they also include npm, Node’s package manager.
Now, type in your terminal:
$ npm install stylus -g
npm でインストールしました。
$ npm install stylus --save-dev
グローバルにはインストールしたくなかったので、 --save-dev
をつけてローカルにインストールしました。
実行するときには npx
を使おうと思っています。
Try Stylus Online!
Try Stylus Online! を順番に見ていきたいと思います。 このページで編集しながらコンパイルもできてしまうようで便利です。 Node.js で使えるくらいだからブラウザー(JavaScript)でも動かせてしまうのは当然と言えば当然かな。
Nesting
/* Stylus */
body {
font: 14px/1.5 Helvetica, arial, sans-serif;
#logo {
border-radius: 5px;
}
}
/* CSS */
body {
font: 14px/1.5 Helvetica, arial, sans-serif;
}
body #logo {
border-radius: 5px;
}
body #logo
がネストされています。
Nesting は便利で使いやすそうです。
Flexible syntax
/* Stylus */
body
font 14px/1.5 Helvetica, arial, sans-serif
button
button.button
input[type='button']
input[type='submit']
border-radius 5px
/* CSS */
body {
font: 14px/1.5 Helvetica, arial, sans-serif;
}
body button,
body button.button,
body input[type='button'],
body input[type='submit'] {
border-radius: 5px;
}
;
と :
と {}
が省略できるようです。
SASS っぽいようです。
Flexible syntax も便利で使いやすそうです。
Parent reference
/* Stylus */
ul
li a
display: block
color: blue
padding: 5px
html.ie &
padding: 6px
&:hover
color: red
/* CSS */
ul li a {
display: block;
color: #00f;
padding: 5px;
}
html.ie ul li a {
padding: 6px;
}
ul li a:hover {
color: #f00;
}
&
で親の要素を参照できるようです。
Parent reference も便利で使いやすそうです。
Mixins
/* Stylus */
border-radius(val)
-webkit-border-radius: val
-moz-border-radius: val
border-radius: val
button {
border-radius(5px);
}
/* CSS */
button {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
再利用可能な関数のようなものが定義しておけるようです。
border-radius
という名前は、 CSS のプロパティの名前なのか、 Mixin の名前なのか、混乱しそうに感じます。
border-radius()
のように、呼び出すときに ()
をつけると Mixin として判断してくれるのかな。
CSS に慣れていないせいか、どこまでを Mixin として定義すればよいのかが難しかったです。 中途半端に共通化した定義をすると、全体的に見通しが悪く、メンテナンスするのが大変そうに感じてしまいました。
Mixins はすぐに使いこなせそうです。
Transparent mixins
/* Stylus */
border-radius()
-webkit-border-radius: arguments
-moz-border-radius: arguments
border-radius: arguments
button {
border-radius: 5px 10px;
}
/* CSS */
button {
-webkit-border-radius: 5px 10px;
-moz-border-radius: 5px 10px;
border-radius: 5px 10px;
}
透明な Mixins かな?
Mixin は呼び出すときに ()
は必要ではないようでした。
border-radius: 5px 10px;
のように、 CSS のプロパティと同じように記述できるようでした。
:
と ;
を省略して、 border-radius 5px 10px
のようにも記述できるようです。
そうなると、この記述は、 CSS のプロパティの border-radius
を直接的に設定する記述なのか、 Mixin の border-radius
を設定する記述なのか、やっぱり混乱しそうです。
命名規則の問題かな。
CSS の命名規則をあまり見たことないですけれども。
arguments
ですべての引数を受け取れるようです。
making it easy to provide cross-browser support
ブラウザー間のサポートを簡単に提供できる、と記載されていましたので、そういう目的のために Mixin を使えばいいのかな。
-webkit-
や -moz-
の指定をするために。
逆に、そういうこと以外に積極的に Mixin を使わなくてもいいのかな。
Transparent mixins もすぐに使いこなせそうです。
Variables
/* Stylus */
#prompt
position: absolute
top: 150px
left: 50%
width: w = 200px
margin-left: -(w / 2)
/* CSS */
#prompt {
position: absolute;
top: 150px;
left: 50%;
width: 200px;
margin-left: -100px;
}
変数が使えるようです。
width: w = 200px
のように、 CSS のプロパティを設定しながら、同時に、変数も定義することができるようです。
ここでは w
という変数が定義されているようです。
may optionally be prefixed by the “$” character:
任意に "$"
の文字をつけることができるようです。
なので、 $w
の変数の名前にすると、変数であることがわかりやすくなりそうです。
Variables は便利で使いやすそうです。
Block property access
/* Stylus */
#prompt
position: absolute
top: 150px
left: 50%
width: 200px
margin-left: -(@width / 2)
/* CSS */
#prompt {
position: absolute;
top: 150px;
left: 50%;
width: 200px;
margin-left: -100px;
}
現在のブロック内のプロパティにアクセスできるようです。
@
をつけるだけのようです。
@width
と記述すると、ブロック内の width
プロパティにアクセスできるようです。
Block property access も便利で使いやすそうです。
Robust feature-rich language
/* Stylus */
-pos(type, args)
i = 0
position: unquote(type)
{args[i]}: args[i + 1] is a 'unit' ? args[i += 1] : 0
{args[i += 1]}: args[i + 1] is a 'unit' ? args[i += 1] : 0
absolute()
-pos('absolute', arguments)
fixed()
-pos('fixed', arguments)
#prompt
absolute: top 150px left 5px
width: 200px
margin-left: -(@width / 2)
#logo
fixed: top left
/* CSS */
#prompt {
position: absolute;
top: 150px;
left: 5px;
width: 200px;
margin-left: -100px;
}
#logo {
position: fixed;
top: 0;
left: 0;
}
これは少し難しかったです。
#prompt
からは absolute()
経由で -pos()
が呼び出されています。
#logo
からは fixed()
経由で -pos()
が呼び出されています。
Stylus の ‘string’ で受け取った文字列を CSS のプロパティに設定する場合は、 unquote(type)
のように、 unquote(str | ident) という Built-in Function を使うようです。
'
や "
を取り除いてくれるようです。
CSS のプロパティの名前として変数を展開する場合は、 {args[i]}
のように、 Interpolation を使うようです。
配列にアクセスする場合は、 []
のように、 Subscript [] オペレーターを使うようです。
変数の型をチェックする場合は、 args[i + 1] is a 'unit'
のように、 Instance Check: is a オペレーターを使うようです。
ここで言う型は、 Built-in Functions の typeof(node) で確認できるようで、いくつか確認した結果を記載しておきます。
node | typeof(node) |
---|---|
test | ‘ident’ |
width | ‘ident’ |
‘absolute’ | ‘string’ |
‘fixed’ | ‘string’ |
150 | ‘unit’ |
150% | ‘unit’ |
150em | ‘unit’ |
150pt | ‘unit’ |
150px | ‘unit’ |
150rem | ‘unit’ |
-(@width / 2) | ‘unit’ |
#fff | ‘rgba’ |
white | ‘rgba’ |
Stylus でも args[i + 1] is a 'unit' ? args[i += 1] : 0
のように、 Conditional Assignment: ?= := オペレーターが使えるようです。
- Conditional (ternary) Operator - JavaScript | MDN
- ?: Operator (C# Reference) | Microsoft Docs
- Conditional Operator: ? :
三項演算子。プログラミング言語では、 Conditional Operator や ternary Operator と呼ぶのかな。
Robust feature-rich language は、この例だけ見てしまうと、見通しがあまりよくなく、メンテナンスしづらい印象を受けてしまいました。 すぐには使いこなせなそうです。
Iteration
/* Stylus */
table
for row in 1 2 3 4 5
tr:nth-child({row})
height: 10px * row
/* CSS */
table tr:nth-child(1) {
height: 10px;
}
table tr:nth-child(2) {
height: 20px;
}
table tr:nth-child(3) {
height: 30px;
}
table tr:nth-child(4) {
height: 40px;
}
table tr:nth-child(5) {
height: 50px;
}
繰り返しができるようです。
Range .. … オペレーターを使って、 for row in 1..5
のようにも記述できるようです。
Iteration は便利そうです。 けれども、自分にとっては、あまり使うことはなさそうです。
Interpolation
/* Stylus */
vendors = webkit moz o ms official
border-radius()
for vendor in vendors
if vendor == official
border-radius: arguments
else
-{vendor}-border-radius: arguments
#content
border-radius: 5px
/* CSS */
#content {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-o-border-radius: 5px;
-ms-border-radius: 5px;
border-radius: 5px;
}
Interpolation は “補間” という意味があるようです。
この例は、 -{vendor}-
のことを指しているのかな。
Stylus はブラウザー間のサポートのために複雑な機能を持っているように感じてきました。
-webkit-
とかって、 2018 年の現在も記述する必要があるのかな。
もうないんじゃないかという印象も受けているのですけれども。
あるいは、こういう記述をするほど複雑なものって必要なのかなとか思っています。
自分にとっては、あまり使うことはなさそうです。
Operators
/* Stylus */
body
foo: 5px + 10
foo: 2 ** 8
foo: 5px * 2
foo: !!''
foo: foo and bar and baz
foo: foo or bar or baz
foo: 1..5
foo: 1...5
foo: 'foo' is a 'string'
foo: (1 2 3) == (1 2 3)
foo: (1 2 3) == (1 2)
foo: ((one 1) (two 2)) == ((one 1) (two 2))
foo: ((one 1) (two 2)) == ((one 1) (two))
foo: ((one 1) (two 2))[0]
foo: 3 in (1 2 3 4)
/* CSS */
body {
foo: 15px;
foo: 256;
foo: 10px;
foo: false;
foo: baz;
foo: foo;
foo: 1 2 3 4 5;
foo: 1 2 3 4;
foo: true;
foo: true;
foo: false;
foo: true;
foo: false;
foo: one 1;
foo: true;
}
オペレーターは Operators を参考にします。
Operators は便利そうです。 自分でも加減算は使うこともありそうかな。
Type coercion
/* Stylus */
body
foo: foo + bar
foo: 'foo ' + bar
foo: 'foo ' + 'bar'
foo: 'foo ' + 5px
foo: 2s - 500ms
foo: 5000ms == 5s
foo: 50deg
/* CSS */
body {
foo: foobar;
foo: 'foo bar';
foo: 'foo bar';
foo: 'foo 5px';
foo: 1.5s;
foo: true;
foo: 50deg;
}
型を強制することについて記載されているようです。
これを見ると、 2s
や 500ms
とかは ‘unit’ 型のようで、文字列としては扱われず、 -
オペレーターで減算することができるようです。
型の扱いは注意しておいた方が良さそうです。
The sprintf operator
/* Stylus */
body
foo: '%s / %s' % (5px 10px)
foo: 'MS:WeirdStuff(opacity=%s)' % 1
foo: unquote('MS:WeirdStuff(opacity=1)')
/* CSS */
body {
foo: 5px / 10px;
foo: MS:WeirdStuff(opacity=1);
foo: MS:WeirdStuff(opacity=1);
}
sprintf
のようなことができるようです。
sprintf
は C 言語の構文だったかな。
%
を使った書式に従った文字列を出力することができるようです。
あまり使うところがイメージできないです。
Color operations
/* Stylus */
body
foo: white - 50%
foo: black + 50%
foo: #eee - #f00
foo: #eee - rgba(black,.5)
foo: #cc0000 + 30deg
/* CSS */
body {
foo: #808080;
foo: #808080;
foo: #0ee;
foo: rgba(238,238,238,0.5);
foo: #c60;
}
Color に対して加減算できるようです。
加減算する値は、 ‘unit’ 型の 50%
とかでもいいみたいですし、 ‘rgba’ 型の rgba(black,.5)
とかでもいいみたいです。
deg
って、初めて知ったのですが、 CSS3 のゆがみを表す単位のようでした。
それも Color に対して加減算することができるようです。
-50%
とか + 50%
とかは使うこともありそうかな。
Functions
/* Stylus */
sum(nums...)
n = 0
n += num for num in nums
body
foo: sum(1, 2, 3)
/* CSS */
body {
foo: 6;
}
何か値を返す関数を定義することができるようです。
mixin と同じ方法で定義することができると記載されていました。 Function は値を返すところが Mixin と違う、とも記載されていました。
これを見ると、最後に設定した変数の値が返されるのかな。 というより、最後に評価した式の値が返されるのかな。
あまり使うところがイメージできないです。
Keyword arguments
/* Stylus */
fade-out(color, amount = 50%)
color - rgba(0,0,0,amount)
body
foo: fade-out(#eee)
foo: fade-out(#eee, 20%)
foo: fade-out(#eee, amount: 50%)
foo: fade-out(color: #eee, amount: 50%)
foo: fade-out(amount: 50%, #eee)
foo: fade-out(amount: 50%, color: #eee)
/* CSS */
body {
foo: rgba(238,238,238,0.5);
foo: rgba(238,238,238,0.8);
foo: rgba(238,238,238,0.5);
foo: rgba(238,238,238,0.5);
foo: rgba(238,238,238,0.5);
foo: rgba(238,238,238,0.5);
}
キーワード引数があるようです。
fade-out(amount: 50%, #eee)
のように、引数の順番を無視できることに意味があるようです。
Mixin と合わせて使うのかな。
Built-in functions
/* Stylus */
body
foo: red(#fc0)
foo: green(#fc0)
foo: blue(#fc0)
foo: alpha(#fff)
foo: alpha(rgba(#fff, .5))
/* CSS */
body {
foo: 255;
foo: 204;
foo: 0;
foo: 1;
foo: 0.5;
}
組み込み関数は Built-in Functions を参考にします。
red(#fc0)
とかは、指定された色の赤の成分を返す組み込み関数のようです。
これはどういうときに必要なのだろう?
Built-in functions はほかにもたくさんあったので、使うこともありそうです。
Color BIFs
/* Stylus */
body
foo: light(#eee)
foo: dark(#eee)
foo: dark(#333)
foo: darken(#eee, 50%)
foo: lighten(black, 50%)
foo: #eee - 50%
foo: black + 50%
/* CSS */
body {
foo: true;
foo: false;
foo: true;
foo: #777;
foo: #808080;
foo: #777;
foo: #808080;
}
Color に関する Built-in Functions のようです。
darken(#eee, 50%)
や lighten(black, 50%)
は SASS, SCSS で使ったことがありました。
Color operations のところにも記載されていましたが、こちらにも #eee - 50%
のような Color に対する加減算の記載がありました。
これは Built-in Functions でもあるようです。
Color BIFs は便利で使いやすそうです。
End
That’s it for now! To view the rest of the powerful features Stylus provides click “View Documentation” in the corner.
come-back later to-see: more unquote(':)')
come-back later { to-see: more :); }
Try Stylus! が一通り終わりました。
Executable
コンパイルについて引用しておきます。 Stylus のファイルの拡張子は .styl のようです。
STDIO
stylus reads from stdin and outputs to stdout, so for example:
$ stylus --compress < some.styl > some.css
標準入出力のリダイレクトを使ってコンパイルできるようです。
<
で Stylus のファイルを読み込んで、 >
で CSS のファイルを出力しているようです。
npx で実行するなら、 Stylus をインストールしたディレクトリーに移動して、次のようになるかな。
$ npx stylus --compress < some.styl > some.css
Files
stylus also accepts files and directories. For example, a directory named css will compile and output .css files in the same directory.
$ stylus css
The following will output to ./public/stylesheets:
$ stylus css --out public/stylesheets
オプションなしの引数に Styles のファイル(ディレクトリー)を指定して、 --out
オプションの引数に CSS のファイル(ディレクトリー)を指定するようです。
npx で実行するなら、 Stylus をインストールしたディレクトリーに移動して、次のようになるかな。
$ npx stylus css --out public/stylesheets
終わり
Try Stylus! を見ながらこの記事を書いてみましたが、書きながら調べたりもしまして、ただ眺めるだけの Try Stylus! よりは少しだけ理解が深まったと思います。
自分で作った Hugo のテーマでは、シンプルにしたかったので、ここに記載した機能のほとんど使いませんでした。