はじめまして。こんにちは。
スマートフォン版Amebaプラットフォームでフロントエンドの開発を担当している遠藤と申します。
今回は、新しくなったAmebaのCSSに関するお話を簡単にさせていただこうと思います。
Stylus
最近多く聞くようになったCSSメタ言語は、SassやLESSが有名ですが、今回はStylusというものを採用しました。
新しいAmebaはnode.jsを採用しており、当初HTMLテンプレートの選定でexpressモジュールのデフォルトでも使われているJadeが候補となり、それと同時にCSSではStylusが挙がりました。
JadeとStylusは作者が同じexpressモジュールの開発者であるため、expressモジュールと相性がよく、記法が似ていてタブによる階層構造で記述するのが特徴です。
Stylusは初めて触りましたが、SassとLESSが持っている機能は基本的に兼ね備えており、コードの見通しの良さと、変更のしやすさから採用を決定しました。
※Stylus
サンプルコード(Stylus)
com-bdc = #ccc
com-bd = solid 1px com-bdc
fr = 4px
// mixin
vendor(prop, args)
-webkit-{prop} args
{prop} args
border-radius(var)
vendor('border-radius',var)
// style
.extend_sample_class
display block
border com-bd
.sample_box
@extend .extend_sample_class
border-radius(fr)
&.heading
border-bottom com-bd
.sample_container &
background red
最初は見慣れないので書き辛いように感じますが、慣れてくると{}や:;を書かなくてもいいことと、セレクタの親子関係をタブ一つで変えることができるので、コーディングが非常に楽になります。
サンプルコード(CSS)
display:block;
border:solid 1px #ccc;
}
.sample_box{
-webkit-border-radius:4px;
border-radius:4px;
}
.sample_box.heading{
border-bottom:solid 1px #ccc;
}
.sample_container .sample_box{
background:#f00;
}
StylusとCSSファイル
Stylusのファイル構造を以下のようにすることで、変更があった際に対応しやすくしています。
基本的に追加・改修が頻繁に発生するため、可能な限り共通化を行い、ユニークなレイアウトのみ、各画面用のstylusに記述する形をとっています。
- ノーマライズ
各ブラウザ間で相違のあるstyleの共通化 - 共通変数&mixin&extend
共通で使う色やサイズの変数、mixin、extendの定義 - コンポーネント
共通で使うCSSコンポーネント定義 - 共通モジュール
共通で使うCSSモジュール定義 - 共通全体import
共通系stylusのimport - 各画面用スタイル
各画面ごとの特殊なstyleの定義
StylusファイルとCSSファイル構成イメージ
node.js+expressモジュールを使う前提であれば、Stylusのコンパイルはnodeで行えますが、そういう環境ではない場合、Macの方にはCodeKitというツールがお勧めです。
CSS系ではStylusだけでなく、SassやLESSもコンパイルできますし、CofeeScriptやHTMLテンプレートのJadeやHamlなども対応しています。
liveReloadも行なってくれるので、かなり開発が楽になります。
※CodeKit
サムネイル画像
ユーザやアプリなどのサムネイル画像の表示方法ですが、今回はimg要素ではなく、i要素に背景として設定するようにしています。
サムネイルの上にオンライン表示のマークや編集アイコンをかぶせたり、サムネイル自体にCSSで装飾を施す場合、img要素は空要素なのでCSSの擬似要素である::beforeや::afterを使って要素の追加をすることができません。
また、画像のサイズ変更の際に、img要素にしていると変倍されてしまったり表示崩れの原因になることもあります。
マークアップとしては本来img要素にすべきですが、デザイン変更を柔軟に行うためにi要素の背景としました。
i要素は代替音声や気分などを表す要素で、img要素の代替として使用するのに適していると判断しました。
デザイン
サンプルコード(Stylus)※一部抜粋
border-radius(size)
vendor('border-radius',size)
box-sizing(prop)
vendor('box-sizing',prop)
square(size)
height unit(size,'px')
width @height
.thumbnail
display inline-block
box-sizing(border-box)
square(50)
border solid 1px #ddd
background-color #fff
background-size cover
background-repeat no-repeat
background-position center center
border-radius 2px
&.online
&.offline
position relative
&::after
content ''
position absolute
bottom 1px
left auto
right -4px
display block
box-sizing(border-box)
height 14px
width @height
margin 0 5px -1px 0
border solid 1px #fff
background-color #ccce
border-radius(@height/2)
&.online
&::after
background #6fc10d
&.large
square(100)
border-radius 4px
&.small
square(40)
サンプルコード(CSS)
.thumbnail {
display: inline-block;
-webkit-box-sizing: border-box;
box-sizing: border-box;
height: 50px;
width: 50px;
border: solid 1px #ddd;
background-color: #fff;
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
-webkit-border-radius: 2px;
border-radius: 2px;
}
.thumbnail.online,
.thumbnail.offline {
position: relative;
}
.thumbnail.online::after,
.thumbnail.offline::after {
content: '';
position: absolute;
bottom: 1px;
left: auto;
right: -4px;
display: block;
-webkit-box-sizing: border-box;
box-sizing: border-box;
height: 14px;
width: 14px;
margin: 0 5px -1px 0;
border: solid 1px #fff;
background-color: rgba(204,204,204,0.933);
-webkit-border-radius: 7px;
border-radius: 7px;
}
.thumbnail.online::after {
background: #6fc10d;
}
.thumbnail.large {
height: 100px;
width: 100px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
.thumbnail.small {
height: 40px;
width: 40px;
}
アイコン
サイトを彩る上で欠かせないアイコンですが、スマートフォンの場合、解像度別に画像を作成して、配置を調整して…とやると、変更、追加が多いサービスでは運用しづらくなってしまいがちです。
また、画像が多くなるにつれて、スプライトで実装をしても、画像ファイルが重くなり、あとから表示されてしまいます。
これを多少でも改善するため、アイコンをフォントで作成しました。
デザイン
これにより、大きさや色の違うアイコンであっても、作り直す必要もなくなり、重くなり過ぎて後から表示されるようなことも改善されています。
コードは、ちょっと不恰好ですが、各文字のクラスに対応した文字が入るものを用意しました。
記号に当たるものに限り、名称をつけています。
サンプルコード(Stylus)※一部抜粋
marks = ('footstamp' '"')('add' '+')('back' '<')('allow' '>')('crown' '¥')('alert' '!');
.i
display inline-block
font-family 'AmebaSymbols'
font-size 2.0rem
line-height 1
.icon
@extend .i
color tc-default !important
vertical-align -.2em
for t in symbols
&.{t}::before
content t
for key,index in marks
&.{key}::before
content key[1]
サンプルコード(CSS)
display:inline-block;
font-family:'AmebaSymbols';
font-size:2rem;line-height:1;
}
.icon{vertical-align:-.2em;}
.icon.a::before{content:"a"}
.icon.b::before{content:"b"}
.icon.c::before{content:"c"}
.icon.d::before{content:"d"}
.icon.e::before{content:"e"}
.icon.f::before{content:"f"}
.icon.g::before{content:"g"}
.icon.h::before{content:"h"}
.icon.i::before{content:"i"}
.icon.j::before{content:"j"}
.icon.footstamp::before{content:'"'}
.icon.add::before{content:'+'}
.icon.back::before{content:'<'}
.icon.allow::before{content:'>'}
.icon.crown::before{content:'¥'}
.icon.alert::before{content:'!'}
スプライト画像の扱い
スマートフォンサイトでは、画像を最適に表示するために、1倍、1.5倍、2倍のサイズ違いの画像を用意剃る必要があります。
装飾で使う画像はCSSスプライトという技法を用いることが多いですが、変更や追加で画像サイズに変更がある場合、CSSのバックグラウンドサイズも変えなければなりません。
Stylusには、画像のサイズを取得するメソッドが用意されているため、これを採用して運用コストを減らしました。
また、最適な解像度の画像の選択ですが、メディアクエリを用いる方法では、対象端末以外では無駄なコードになってしまうため、javascriptを使って、pixel ratioを取得し、CSSを書き換えることで最適な画像を表示するようにしています。
サンプルコード(Stylus)
bg(name)
path = '../img/$ratio/external/'+name
overflow hidden
background-image url(path)
background-size image-size(image-path + '/external/'+ name) style="color:#f00">// 画像のサイズを取得
background-repeat no-repeat
.bg-sprite
bg('amb_bg_sprite.png')
text-indent 100%
.ameba
display block
height 18px
width 85px
@extend .bg-sprite
background-position 0 0
サンプルコード(CSS)
overflow:hidden;
background-image:url("../img/$ratio/amb_bg_sprite.png"); // $ratioをjavascriptで端末のpixelratioに書き換え
background-size:285px 65px;
background-repeat:no-repeat;
text-indent:100%;
}
サンプルコード(Javascript)
var pixelRatio = window.devicePixelRatio || 1;
// find target css
var stylesheets = document.styleSheets;
for (var i = 0; i < stylesheets.length; i++) {
var stylesheet = stylesheets[i];
if (stylesheet.ownerNode.getAttribute('href') == href) {
var rules = stylesheet.cssRules;
for (var j = 0; j < rules.length; j++) {
var rule = rules[j];
var style = rule.style;
if (style) {
var bgimage = style.getPropertyValue('background-image');
if (bgimage && bgimage.indexOf('$ratio') >= 0) {
rule.style.setProperty('background-image', bgimage.replace(/\$ratio/, pixelRatio), null);
}
}
}
}
}
}
最後に
細かい点は色々と端折らせていただきましたが、このような感じでCSSを構築しております。
今回のプラットフォームの構築にあたり、構築過程で大きなデザイン変更が3度ほどありましたが、こういった設計によって、基本的にCSSの変更だけで対応できています。
Stylusに関しては、初めて使用しましたが、Sass+Compassほどの高機能でもなく、LESSよりやれることが多いといった印象で、個人的には非常に使いやすく感じました。
記法への慣れは必要ですが、:や;や{}を書かなくて済むのと、コードが非常に見やすくなるので、今後は積極的に使ってみようと思っています。