第8回 実例で学ぶjQuery MobileのCSSカスタマイズ

jQuery Mobileを利用するとスマートフォンサイトを手軽に制作できますが、基本テンプレートをベースに制作すると「いかにもjQuery Mobileらしい」デザインになってしまい、他のサイトと似通ってしまいます。

 そこで今回はjQuery Mobileのテンプレートをフルカスタマイズし、オリジナルデザインのスマートフォンサイトを作成する方法を解説します。サンプルとして、次のようなカタログサイトを制作します。

今回作成するサンプルサイトの概要。画像をクリックするとサンプルサイトを表示します

 トップページには新着商品のサムネイル画像が3つ並び、その下にはアコーディオンパネルでニュースを掲載しています。新着商品のサムネイル画像を選択すると詳細ページに遷移し、詳細ページで画像をタップすると画像を拡大して表示します。

HTMLの作成

 デザインカンプを元にHTMLを作成します。head要素内は次のように記述します。

■サンプル1[HTML]


<!DOCTYPE html> 
<html> 
  <head> 
    <meta charset="UTF-8">
    <title>jQuery Mobile Sample</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css" />
    <script src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
    <script src="http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.js"></script>
  </head> 
  <body>
  (中略)
  </body>
</html>

 meta要素のviewportには「width=device-width, initial-scale=1」を指定し、横幅を端末の画面幅に、初期の拡大率を100%に設定します。また、jQuery Mobileに必要なjQuery本体とjQuery Mobileのスクリプトファイル、jQuery Mobileのスタイルシートを読み込んでおきます(jQuery Mobileの執筆時のバージョンはベータ3)。

 body要素の内側には次のようなHTMLを記述します。

■サンプル1[HTML]


<!--トップページ-->
<div data-role="page" id="index" data-theme="z">
  <div data-role="header" data-theme="z">
    (中略)
  </div>
  <div data-role="content">
    (中略)
  </div>
  <div data-role="footer" data-theme="z">
    (中略)
  </div>
</div>
<!--詳細ページ-->
<div data-role="page" id="detail" data-theme="z">
  <div data-role="header" data-theme="z">
    (中略)
  </div>
  <div data-role="content">
    (中略)
  </div>
  <div data-role="footer" data-theme="z">
    (中略)
  </div>
</div>

 トップページは「#index」というページを、詳細ページは「#detail」というページをdata-role属性の「page」で設定しています(サンプルサイトでは解説の都合上、すべての商品の詳細ページを#detailにしていますが、実際にはそれぞれ個別のページを作成する必要があります)。

 各ページ内にはdata-role属性「header」でヘッダーを、data-role属性「content」でコンテンツ領域を、data-role属性「footer」でフッターを作成します。また、ページ要素とヘッダー、フッターにはdata-theme属性「z」を指定します。「z」はjQuery Mobileのデフォルトテーマである「a」〜「e」以外の値なら何でも構いません。デフォルトのテーマの値以外を設定すると、jQuery Mobileの基本的なスタイルのみが適用され、オリジナルのスタイルが楽に設定できます。

ページ内の領域をpage-role属性で指定する

 全体の構造がつかめたら、各ページのマークアップを見ていきましょう。

トップページのマークアップ

 トップページのヘッダーは以下のように記述します。

■サンプル1[HTML]


<div data-role="header" data-theme="z">
  <h1>HOUSEPLANT STORE</h1>
  <h2>I LOVE HOUSEPLANT</h2>
  <p>sample text sample text sampletext sample text sample text sample text</p>
</div>

 ヘッダーではh1要素でサイト名を、h2要素でキャッチコピーを、p要素でサイトの紹介文をマークアップします。

ヘッダーのマークアップ

 トップページのコンテンツ領域は以下のようにマークアップします。

■サンプル1[HTML]


<div data-role="content">
  <h2>RECOMMEND</h2>
  <!--新着商品-->
  <div class="ui-grid-b recommend">
    <div class="ui-block-a">
      <a href="#detail" class="new">
        <img src="./images/thumbnail01.jpg" width="82" height="70" alt="">
      </a>
    </div>
    <div class="ui-block-b">
      <a href="#detail">
        <img src="./images/thumbnail02.jpg" width="82" height="70" alt="">
      </a>
    </div>
    <div class="ui-block-c">
      <a href="#detail">
        <img src="./images/thumbnail03.jpg" width="82" height="70" alt="">
      </a>
    </div>
  </div>
  <!--アコーディオン-->
  <div data-role="collapsible-set">
    <div data-role="collapsible" data-collapsed="true">
      <h3>SITE MENU 1</h3>
      <p>sample text sample text sampletext sample text sample text sample text</p>
      <p>sample text sample text sampletext sample text sample text sample text</p>
    </div>
    <div data-role="collapsible" data-collapsed="true">
      <h3>SITE MENU 2</h3>
      <p>sample text sample text sampletext sample text sample text sample text</p>
      <p>sample text sample text sampletext sample text sample text sample text</p>
    </div>
    <div data-role="collapsible" data-collapsed="true">
      <h3>SITE MENU 3</h3>
      <p>sample text sample text sampletext sample text sample text sample text</p>
      <p>sample text sample text sampletext sample text sample text sample text</p>
    </div>
    <div data-role="collapsible" data-collapsed="true">
      <h3>SITE MENU 4</h3>
      <p>sample text sample text sampletext sample text sample text sample text</p>
      <p>sample text sample text sampletext sample text sample text sample text</p>
    </div>
  </div>
</div>

 見出しをh2要素でマークアップし、新着商品はグリッドレイアウト(関連記事)の機能を利用してマークアップします。3カラムのグリッドにするclass属性「ui-grid-b」を指定した要素の中に、class属性「ui-block-a」〜「ui-block-c」を指定した要素を配置します。グリッド内にはimg要素と詳細ページ(#detail)をhref属性に指定したa要素を指定します。

コンテンツ領域のマークアップ

 アコーディオンパネルは、data-role属性「collapsible-set」を指定した要素の内側に、data-role属性「collapsible」の要素を配置して作ります。各パネルにはdata-collapsed「true」を指定し、デフォルトでは閉じた状態にしています(関連記事)。

 最後に、フッターにsmall要素でコピーライトを表示します。small要素は免責条項や著作権表記などの細目を表すHTML5の要素です。

■サンプル1[HTML]


<div data-role="footer" data-theme="z">
  <small>Copyright &copy; 2011 ASCII MEDIA WORKS. All rights reserved.</small>
</div>

詳細ページのマークアップ

 続いて、詳細ページをマークアップします。

■サンプル1[HTML]


  <div data-role="page" id="detail" data-theme="z">
    <div data-role="header" data-theme="z">
      <h1><a href="#index" data-rel="back">HOUSEPLANT STORE</a></h1>
      <h2>I LOVE HOUSEPLANT</h2>
    </div>
    <div data-role="content">
      <h2>ITEMS</h2>
      <p><a href="#img" data-rel="dialog" data-transition="pop"><img src="./images/thumbnail01.jpg" width="82" height="70" alt="" align="left"></a>sample text sample text sampletext sample text sample text sample text sample text sample text</p>
  (中略)
      <p>sample text sample text sampletext sample text sample text sample text sample text sample text</p>
    </div>
    <div data-role="footer" data-theme="z">
      <small>Copyright &copy; 2011 ASCII MEDIA WORKS. All rights reserved.</small>
    </div>
  </div>

 ヘッダーのh1要素はa要素で包み、data-rel="back"を指定すると、前のページへスライドして戻ります。href属性の値は動作に影響しませんが、JavaScriptがオフの環境も考慮して「#index」を指定しておきます。

 コンテンツ領域では、商品画像をa要素で包み、data-rel属性「dialog」、data-transition属性「pop」を指定します。これで、画像がタップされるとダイログがポップアップして拡大画像が開きます(関連記事)。

 ダイアログページは次のようなHTMLになっています。

■サンプル1[HTML]


<div data-role="page" id="img" class="modal" data-theme="z">
  <a href="#detail" data-rel="back"><img src="./images/image01.jpg" alt=""></a>
</div>

 画像にdata-rel属性「back」を指定し、タップされたらダイアログを閉じて元のページに戻るように設定しています。

 以上でHTMLは完成です。スマートフォンで確認すると次のように表示されます (sample02.html)。

02-1.jpg
02-2.jpg

 このHTMLにCSSを適用していきます。

CSSの作成

 まずはヘッダーのCSSから作成しましょう。前に記述したHTMLは次のような内容でした。

■サンプル1[HTML]


<div data-role="page" id="index" data-theme="z">
  <div data-role="header" data-theme="z">
    <h1>HOUSEPLANT STORE</h1>
    <h2>I LOVE HOUSEPLANT</h2>
    <p>sample text sample text sampletext sample text sample text sample text</p>
  </div>
  (中略)
</div>

 ところが、このページをSafariで開き、開発者メニューからWebインスペクタを起動すると、次のように表示されます。

Webインスペクタで確認したHTML

 前のHTMLと比べると、class属性をはじめとするさまざまな属性が追加されているのが分かります。

 jQuery Mobileは、カスタムデータ属性などの値に応じてJavaScriptでHTMLの内容を書き換え、実際に表示されるページを生成しています。場合によっては、原型をとどめないほどHTMLが書き換えられることもありますので、CSSはWebインスペクタなどで実際のHTMLを確認しながら記述しましょう。

参考:Safariの開発者メニューでスマートフォン開発
http://blog.webcreativepark.net/2011/05/12-213145.html

 ヘッダーのCSSは次のようになります。

■サンプル2[CSS]


/*
.ui-header
-------------------*/
.ui-body-z .ui-header{    ……【1】
  background-image:url("./images/header-background.jpg");
  -webkit-background-size:100% 104px;    ……【2】
  background-size:100% 104px;    ……【2】
  position:relative;
  height:104px;
  z-index:1;
}
.ui-body-z .ui-header::before{    ……【3】
  content:url("./images/header-flag.png");
  position:absolute;
  left:5px;
  top:0;
}
.ui-body-z .ui-header::after{    ……【3】
  content:url("./images/header-reef-s.png");
  position:absolute;
  right:0px;
  bottom:15px;
}
.ui-body-z .ui-header h1{    ……【4】
  text-indent:-9999px;
  background-image:url("./images/logo.png");
  width:192px;
  height:15px;
  min-height:15px;
  padding:0;
  margin:23px auto 4px;
}
@media only screen and (-webkit-min-device-pixel-ratio: 2){    ……【4】
  .ui-body-z .ui-header h1{
    background-image:url("./images/logo@2.png");
    -webkit-background-size:192px 15px;
    background-size:192px 15px;
  }
}
.ui-body-z .ui-header h2{
  color:#6f5b00;
  font-weight:normal;
  font-size:12px;
  text-align:left;
  margin:0 auto;
  width:192px;
}
.ui-body-z .ui-header p{
  font-size:12px;
  color:#4c8d00;
  margin:0 125px 0 15px;
}
.ui-body-z .ui-content{
  position:relative;
  margin-top:-40px;
  z-index:2;
}
/*
#index .ui-header
-------------------*/
#index.ui-body-z .ui-header{    ……【5】
  background-image:url("./images/header-background-top.jpg");
  background-size:100% 170px;
  height:170px;
}
#index.ui-body-z .ui-header:after{
  content:url("./images/header-reef.png");
  bottom:20px;
}
#index.ui-body-z .ui-header h1{
  margin-bottom:18px;
}
#index.ui-body-z .ui-header h2{
  width:auto;
  margin:0 auto 5px 15px;
}
#index.ui-body-z .ui-content{
  margin-top:-50px;
}

 少しずつ分割してポイントを解説します。ヘッダー全体に関わる部分から見てみましょう。

●.ui-body-zを起点にする

 CSSセレクターは「.ui-body-z .ui-header」といった具合に、冒頭に「.ui-body-z」を付けて記述していきます【1】。


.ui-body-z .ui-header{    ……【1】

 この.ui-body-zは、data-theme属性「z」を指定しているときにページ要素に自動的に付加されるclass属性です。.ui-body-zを起点にすることで、他のテーマ(たとえばdata-theme属性「a」)を適用したいときに、オリジナルテーマである「z」が干渉してデザインが崩れるのを防げます。


●背景画像の調整

 【2】では、端末の向きによって画面幅が変わるスマートフォンの特性に対応するため、ヘッダーの背景に「background-size:100% 104px;」を指定しています。


  -webkit-background-size:100% 104px;    ……【2】
  background-size:100% 104px;    ……【2】

 background-sizeプロパティは、背景画像のサイズを設定するCSS3のプロパティで、2つの値を与えると幅と高さを個別に設定できます。background-size:100% 104pxと指定すると、背景画像の幅は要素に対して100%に調整され、高さは104pxで固定されます。

 多くのスマートフォンのブラウザーでは、portrait(縦向き)のときは横幅320pxで表示されますが、landscape(横向き)にすると端末のディスプレイサイズによって横幅がおおよそ400〜550px程度に変わります。あらかじめ横幅500px程度の大きな背景画像を用意しておく方法もありますが、今回は横幅320pxの画像を使い、横幅が広がったときはそのまま引き伸ばして、読み込むファイルサイズを軽くしています。

landscape時は引き伸ばして表示する

●before擬似要素/after擬似要素による画像の挿入

 【3】では、ヘッダーの左上にある旗のマークの画像と、右下にある葉っぱのマークの画像をcontentプロパティで追加しています。


.ui-body-z .ui-header::before{    ……【3】
  content:url("./images/header-flag.png");
  position:absolute;
  left:5px;
  top:0;
}
.ui-body-z .ui-header::after{    ……【3】
  content:url("./images/header-reef-s.png");
  position:absolute;
  right:0px;
  bottom:15px;
}

 iPhoneやAndroidでは、CSS3のセレクターである「::before」(before擬似要素)や「::after」と(after擬似要素)を利用し、要素の前後にcontentプロパティで画像やテキストを追加できます。

 このとき、.ui-headerに「position:relative」を指定しておくと、追加した画像は「position:aboslute」で絶対位置が指定できます。HTMLは触らずに、画面の横幅に左右されない画像を配置できます。


●Retina displayへの対応

 【4】は、iPhone 4のRetina displayに対応するためのCSSです。Retina displayとはiPhone 4に搭載されている超高解像度ディスプレイのことで、iPhone 3GS以前(320×480px)に比べて倍の解像度(640×960px)の画像を描画できます。


.ui-body-z .ui-header h1{    ……【4】
  text-indent:-9999px;
  background-image:url("./images/logo.png");
  width:192px;
  height:15px;
  min-height:15px;
  padding:0;
  margin:23px auto 4px;
}
@media only screen and (-webkit-min-device-pixel-ratio: 2){    ……【4】
  .ui-body-z .ui-header h1{
    background-image:url("./images/logo@2.png");
    -webkit-background-size:192px 15px;
    background-size:192px 15px;
  }
}

 iPhone 4はiPhone 3GS以前と同じ画像でも問題なく表示しますが、Retinaディスプレイ専用の画像を用意することでより鮮明に表示します。以下の2つの画像の表示を比較すると、専用の画像を用意したほうが非常に鮮明に表示できるのが分かります。

<画像>04-01.jpg / 04-02.jpg
Retina displayに対応した場合(左)と対応しない場合(右)

 Retinaディスプレイに対応するには、CSS3のメディアクエリーを利用して、min-device-pixel-ratioが「2」の端末に対してのみCSSを適用します。min-device-pixel-ratioは端末のピクセル密度を表す値で、iPhone 4やAndroid端末の高解像度端末(IS03など)では値が「2」になります。min-device-pixel-ratioはベンダープリフィックスを付ける必要がありますので注意してください。


@media only screen and (-webkit-min-device-pixel-ratio: 2){
  /*device-pixel-ratioが2の場合の指定*/
}

 メディアクエリーの条件に合致する場合(min-device-ratioが2の場合)は、通常とは別の倍の解像度の画像を指定し、background-sizeで通常サイズに縮小することで、Retinaディスプレイに対応した画像が表示できます。


.ui-body-z .ui-header h1{
  text-indent:-9999px;
  background-image:url("./images/logo.png");/*通常サイズの画像*/
  width:192px;
  height:15px;
}
@media only screen and (-webkit-min-device-pixel-ratio: 2){
  .ui-body-z .ui-header h1{
    background-image:url("./images/logo@2.png");/*倍の解像度の画像*/
    -webkit-background-size:192px 15px;/*通常サイズ*/
    background-size:192px 15px;/*通常サイズ*/
  }
}

 なお、Android端末では「device-pixel-ratio」が1.5のデバイスも多数存在しますが、1.5倍の解像度では計算値が複雑になるので、「device-pixel-ratio」が2の場合だけ指定するのが一般的です。

 また、Retinaディスプレイに対応すると倍の解像度の画像が表示されることになるので、ファイルサイズは元の画像の4倍近くになり、読み込みに時間がかかってしまいます。そのため、ロゴ部分など、どうしても綺麗に見せたいところだけ対応するとよいでしょう。

●特定ページに対するCSSの指定


#index.ui-body-z .ui-header{    ……【5】
  background-image:url("./images/header-background-top.jpg");
  background-size:100% 170px;
  height:170px;
}

 今回のサンプルでは、トップページと詳細ページのヘッダーの内容が異なるので、詳細ページ向けのスタイルは「.ui-body-z」から始まるセレクターに記述し、トップページ向けのスタイルは「#index.ui-body -z」から始まるセレクターに記述することでCSSを上書きしています。ページIDを利用してスタイルを切り分けるテクニックは便利ですので、覚えておくとよいでしょう。

 以上でヘッダー部分のCSSは完成しました。

コンテンツ領域のCSS設定

 続いて、コンテンツ領域のCSSを設定しましょう。ヘッダーと同じように、まずはjQuery MobileによってHTMLがどのように変わっているか確認しましょう。アコーディオンパネルの部分は複雑ですので後ほど解説しますが、それ以外の部分はそれほど変更されていません。


<div data-role="content" class="ui-content" role="main">
  <h2>RECOMMEND</h2>
  <div class="ui-grid-b recommend">
    <div class="ui-block-a">
      <a href="#detail" class="new ui-link">
        <img src="./images/thumbnail01.jpg" width="82" height="70" alt="">
      </a>
    </div>
    <div class="ui-block-b">
      <a href="#detail" class="ui-link">
        <img src="./images/thumbnail02.jpg" width="82" height="70" alt="">
      </a>
    </div>
    <div class="ui-block-c">
      <a href="#detail" class="ui-link">
        <img src="./images/thumbnail03.jpg" width="82" height="70" alt="">
      </a>
    </div>
  </div>
  (中略:アコーディオンパネル部分)
</div>

 CSSは次のようになります。

■サンプル2[CSS]


/*
.ui-content
-------------------*/
.ui-body-z .ui-content h2{
  font-size:14px;
  font-weight:bold;
  color:#5b9a12;
}
.ui-body-z .ui-content p{
  font-size:12px;
  color:#665400;
}
.ui-body-z .ui-content p img{
  border:solid 3px #ae9f59;
  margin:5px;
}
/*
.ui-content .recommend
-------------------*/
.ui-body-z .ui-content .recommend{
  text-align:center;
}
.ui-body-z .ui-content .recommend img{
  border:solid 3px #ae9f59;
}
.ui-body-z .ui-content .recommend a{
  position:relative;
  display:inline-block;
  padding-top:5px;
  text-decoration:none;
}
.ui-body-z .ui-content .recommend a.new::before{    ……【6】
  content:"new";
  display:block;
  font-size:11px;
  color:white;
  background:#a06d24;
  padding:2px 5px;
  position:absolute;
  top:-3px;
  left:10px;
}
.ui-body-z .ui-content .recommend a.new::after{    ……【6】
  content:"";
  display:block;
  width:0;
  border-bottom:3px solid #7e4d06;
  border-left:3px solid #7e4d06;
  border-top:3px solid transparent;
  border-right:3px solid transparent;
  position:absolute;
  top:-1px;
  left:39px;
}

 CSSもそれほど複雑ではありません。ここでは【6】でa要素にclass属性「new」が付いている場合に、「new」というアイコンを表示しているのがポイントです。

<画像>05.png
class属性「new」が付いている場合にのみnewが表示される

 このアイコンはbefore擬似要素とafter擬似要素を利用して表現しています。before擬似要素でアイコンの土台となる矩形のプレートを作成し、after擬似要素で右側の三角形を表現します。擬似要素を利用して空の要素を追加し、そのborderプロパティで三角形を描いており、画像は使用していません。borderを利用した三角形の描画は複雑ですが、便利なテクニックですのでぜひ試してみてください。

 スマートフォンサイトではbefore擬似要素やafter擬似要素を賢く利用することで、HTMLをシンプルに保ったまま、さまざまな表現ができます。

 次回は、アコーディオンパネルとフッターのCSSを作成します。

Comments