次の方法で共有


Cutting Edge

CSS のプログラミング: LESS で効率を上げる

Dino Esposito

今回は、CSS コンテンツの動的な生成に LESS フレームワークを使用する Web 開発について説明します。

Web ページのプレゼンテーションとコンテンツを完全に分離するという (ほぼ達成済みの) 約束によって、CSS が飛躍的に成長したことは間違いありません。CSS はデザイナーの担当 (のはず) ですが、ほぼすべての開発者が気にしている "関心の分離" の原理を尊重しています。そのため、CSS の使用はすぐに普及し、今では最新の Web サイトの進化に対応するのが難しいこともあるほど Web 開発に深くかかわるようになっています。

これは、グラフィックが豊富で魅力的な最新の Web サイトのスタイル設定に CSS では力不足だという意味ではなく、純粋な宣言型言語は、複雑で相互に関連するスタイルの表現に適しているとは限らないという意味です。さいわい、ブラウザーならどれほど複雑な CSS でも正しく記述されている限り理解できます。しかし、人間はそうではありません。

比較的最近の Web 開発では、開発者とデザイナーが同じ CSS をより長期間利用可能な方法で作成できるよう CSS の周囲にインフラストラクチャを構築しようとする動きが生まれています。ブラウザーに提供する最終的なスタイルシートは変わりませんが、スタイルシートの作成方法が、読みやすくて管理しやすくなります。

この分野の Web 開発は数年前に誕生しましたが、動的な CSS コンテンツの生成に役立つフレームワークをいくつも利用できるようになり、成熟しつつあります。今回は、このようなフレームワークの 1 つである LESS フレームワークについて、概要を説明し、ASP.NET MVC ソリューションに統合する方法を紹介します。

LESS を選ぶ理由

開発者が LESS で対処する最大の問題は、情報の繰り返しです。おそらくソフトウェア開発者の方は、"同じことを繰り返さない" (DRY: Don't Repeat Yourself) の原理をご存じで、毎日この原理を実践しているでしょう。DRY の主なメリットは、同一の情報を格納している場所が減るために、同時に更新が必要な場所の数も減ることです。

プレーン CSS では、DRY の問題はありません。たとえば、他のシナリオで、特定の色を複数のクラスで使用していてその色を変更する必要がある場合、基本的に使用場所を 1 か所ずつ更新する以上に優れた方法はありません。CSS クラスを使用すれば、特定の要素の外観を定義でき、定義した外観をページ間で再利用して、関連する要素に同じようにスタイルを設定できます。CSS クラスを使用すると確かに繰り返しを削減できますが、他の面では機能が不十分な場合もあります。

CSS クラスの問題の 1 つは、CSS クラスがセマンティック HTML 要素のレベルで機能することです。さまざまな CSS クラスを作成していると、色や幅などの小さな情報の繰り返しがよく発生します。このような小さい繰り返し可能な情報にそれぞれクラスを作成することは、簡単ではありません。ほぼすべての繰り返し可能なスタイル (色、幅など) に CSS クラスを作成できたとしても、セマンティック要素 (コンテナーなど) のスタイルを設定するとなると、目的の効果を実現するには複数の CSS クラスを連結する必要があります。

Web ページのデザインに Bootstrap などのフレームワークを使用したことがある方は、この意味がおわかりでしょう。以下に例を示します。

<a class="btn btn-primary" ... />

この例では、アンカーがボタン (btn クラス) になるよう設定してから、特定の特徴を持つボタン (btn-primary クラス) になるよう設定しています。このアプローチは機能しますが、必要なクラスの事前計画を立てるためにかなりの作業が必要になることがあります。そのため、納期間近の Web プロジェクトでは、オーバーヘッドの原因になります。

LESS などの動的なスタイルシート言語は、ある種の水平思考を体現しています。プレーン CSS の効率化には時間を割かず、別の手段 (たいていは言語) を使用して CSS を生成します。したがって、LESS はプログラマにとってなじみ深い概念 (変数、ブロック、関数など) を CSS コードに追加するフレームワークです。

動的な CSS の生成に厳密に関係しているのが、動的な CSS をブラウザーで使用できるようプレーン CSS に変換する問題です。クライアントでアドホックな JavaScript コードを使用して LESS コードを変換することもあれば、ブラウザーが最終的な CSS だけを受け取るようにサーバーで LESS コードを前処理することもあります。

ASP.NET MVC で LESS を設定する

では、ASP.NET MVC アプリケーション内で LESS を使用するために必要な準備について説明しましょう。まずは、クライアント側での LESS コードの変換に焦点を当てます。レイアウト ファイルの HEAD セクションに、以下のコードを追加します。

<link rel="stylesheet/less"
  type="text/css"
 href="@Url.Content("~/content/less/mysite.less")" />
<script type="text/javascript"
 src="@Url.Content("~/content/scripts/less-1.3.3.min.js")"></script>

この例では、すべての LESS ファイルを配置する Content/Less フォルダーをプロジェクトに作成してあると想定しています。また、ブラウザー内で実際に LESS から CSS に変換する JavaScript ファイルが必要になります。このスクリプト ファイルは less-ja.studiomohawk.com から入手できます。次のセクションからは、LESS の有用性を実証するシナリオをいくつか紹介します。

LESS の使用例: 変数

LESS の変数について理解するための優れた方法は、CSS のグラデーションについて調べることです。デザイナーは長年、HTML コンテナーの背景にグラデーションを設定する手段として、小さな GIF ファイルを使用していました。ブラウザーにグラデーションの CSS サポートが追加されたのは、つい最近のことです。追加されたグラデーションは、linear-gradient 構文やそのバリエーションを通じて公式な CSS3 標準にも組み込まれています。残念ながら、できる限り多様なブラウザーでグラデーションを表示するには、図 1 のようなコードを使用する必要があります。

図 1 のコードは、ほとんど解読不能です。しかも、グラデーションが必要なすべての場所に繰り返し記述する必要があります。また、グラデーションの色 (または単に彩度や透明度) を少しでも変更しようとすると、すべての使用箇所を手動で編集するしか手段がありません。率直に言って、これはとても大変な作業です。しかし、プレーン CSS でこのグラデーションを適用するには、他に方法がありません。

図 1 多様なブラウザーでグラデーションを表示するための包括的なコード

/* Old browsers fallback */
background-color: #ff0000;
background: url(images/red_gradient.png);
background-repeat: repeat-x;
/* Browser specific syntax */
background: -moz-linear-gradient(  left,  #fceabb 0%, 
  #fccd4d 50%, #f8b500 51%, #fbdf93 100%);
background: -Webkit-linear-gradient(  left, #fceabb 0%,
  #fccd4d 50%,#f8b500 51%,#fbdf93 100%);
background: -o-linear-gradient(  left, #fceabb 0%,
  #fccd4d 50%,#f8b500 51%,#fbdf93 100%);
background: -ms-linear-gradient(  left, #fceabb 0%,
  #fccd4d 50%,#f8b500 51%,#fbdf93 100%);
/* Standard syntax */
background: linear-gradient(  to right, #fceabb 0%,
  #fccd4d 50%,#f8b500 51%,#fbdf93 100%);

もっと優れた解決策を見つけるには、CSS 以外に目を向け、LESS の領域に踏み込む必要があります。LESS では、グラデーションの CSS を定義してから、必要に応じて名前で参照します。以下に例を示します。

.background-gradient-orange { background: #fceabb; ... }
.container { .background-gradient-orange; }

background-gradient-orange というクラスは、必要に応じてクラス コンテナーや他のクラスに名前で埋め込みます。しかし、グラデーションの定義は 1 か所に保持しています。

開発者の視点から見ればこのクラスは少しも革新的ではありませんが、変数という、CSS にはまったく存在しない機能を使用しています。事実、このファイルをプレーン スタイルシートとして保存して参照しても、先ほどの構文は機能しません。この拡張構文をプレーン CSS に変換するには、コードが必要です。LESS JavaScript パーサーによって、この変換が実行され、変数が実際の CSS コンテンツに拡張されます。

変数は、色やサイズなどのスカラー値にも適用できます。次の LESS コードについて考えてみましょう。

@black: #111;
#main {  color: @black; }
.header { background-color: @black; }

パーサーによって、割り当てられている値に @black 変数が拡張され、ファイル全体の @black が置き換えられます。つまり、1 か所で実際の色を変更するだけで、自動的にファイル全体に変更が反映されます。

LESS の使用例: インポート

LESS コードは、必要に応じて複数のファイル、参照ファイル、および包含されているクラスに分割できます。たとえば、以下のコンテンツが含まれた gradients.less ファイルを作成するとします。

.background-gradient-orange { background: #fceabb; ... }

別の、たとえば main.less という LESS ファイルで、次のように gradients.less ファイルをインポートしてグラデーションを参照できます。

@import "gradients";
.container { .background-gradient-orange; }

gradients.less (厳密には拡張子を指定する必要はありません) が他のフォルダーに存在している場合は、インポートの呼び出しでパス情報を指定する必要があります。

LESS のミックスイン

先ほどは、グラデーション用の LESS 成果物を変数と呼びましたが、正確には、この呼び方は完全に適切とは言えません。なぜなら、LESS では変数に 1 回だけ値を格納するためです。CSS クラスのコンテナーは、ミックスインと呼ばれています。ミックスインは関数に似ていますが、カスタム ロジックが含まれていません。ただし、LESS ミックスインでは関数と同じようにパラメーターを受け取って処理できます。ミックスインの使用方法を示す 図 2 のコードについて考えてみましょう。

図 2 では、shadow という名前のミックスインでボックスの影のスタイルを定義し、外部パラメーターとして color を公開しています。同様に、text-box ミックスインでは入力フィールドの基本的な外観を定義します。影の定義をインポートし、width パラメーターを格納します。このようにすると、異なるサイズ (小、標準、大) の入力フィールドに対応する 3 つのクラスを簡単に定義できます。さらに重要なことに、編集が必要になる箇所がごくわずかなので、最小限の労力で更新できます (図 3 参照)。

図 2 LESS フレームワークのミックスイン

/*  Mixins  */
.shadow(@color) {
  box-shadow: 3px 3px 2px @color;
}
.text-box(@width) {
  .shadow(#555);
  border: solid 1px #000;
  background-color: #dddd00;
  padding: 2px;
  width: @width;
}
/*  CSS classes  */
.text-box-mini {
  .text-box(50px);
}
.text-box-normal {
  .text-box(100px);
}
.text-box-large {
  .text-box(200px);
}


図 3 LESS ミックスインの使用例

ミックスインでは複数のパラメーターや、パラメーターの変数も受け取ることができます。さらに、個々のパラメーターでは次のように既定値をサポートしています。

.mixin(@color: #ff0) { ... }

LESS はリッチ プログラミング言語を表現しているわけではないため、条件やループを表すコマンドは設計上存在しません。しかし、渡された値に応じてミックスインの動作を変更することはできます。たとえば、ボタンを大きくし、境界線とフォントを太くする必要があるとします。button というパラメーター形式のミックスインを定義し、"when" キーワードを使用して条件に設定をバインドします。条件は、次のように 1 つのパラメーターに基づいている必要があります。

.button (@size) when (@size < 100px) {
 padding: 3px;
 font-size: 0.7em;
 width: @size *2;
}
.button (@size) when (@size >= 100px) {
  padding: 10px;
  font-size: 1.0em;
  font-weight: 700;
  background-color: red;
  width: @size *3;
}

ここではさまざまな設定を適用しましたが、基本演算を使用してサイズを数倍に乗算することもできます。次は、ミックスインを実際の CSS クラスで使用しましょう。

.push-button-large {
  .button(150px);
}
.push-button-small {
  .button(80px);
}

このコードの実行結果を 図 4 に示します。


図 4 CSS クラスでの LESS ミックスインの使用効果

LESS には、色を操作するための事前に定義された関数が多数用意されています。たとえば、パーセント単位で色の明度、彩度、および (以下のように) 色の透明度を変更する関数があります。

.push-button {
  background-color: fade(red, 30%);
}

LESS でサポートされている関数すべてに関するドキュメントについては、less-ja.studiomohawk.com を参照してください。

クラスを入れ子にする

同レベルのスタイルを示すために CSS ブロックを繰り返す必要がある場合、個人的にはかなりストレスを感じます。典型的な例を次に示します。

#container h1 { ... }
#container p { ... }
#container p a { ... }
#container img { ... }

プレーン CSS をうまく記述すれば大半の繰り返しを回避できますが、並列のリストでスタイルを構成することは最適な手法ではありません。この場合は、少し階層的に構成する方がお勧めです。LESS では、スタイル規則を次のように入れ子にできます。

.container {
  h1 {
    font-size: 0.8em;
   color: fade(#333, 30%);
   a {
     color: #345;
     &:hover {color: red;}
    }
  }
}

前の LESS コードを処理すると、以下のスタイルが作成されます。

.container h1
.container h1 a
.container h1a:hover

サーバー側の処理

LESS コードをそのままの形式でダウンロードし、クライアント上で JavaScript コードを使用して処理することができます。また、LESS コードをサーバー上で前処理し、プレーン CSS としてクライアントにダウンロードすることもできます。クライアント上で処理する場合は、プレーン CSS ファイルを使用している場合とまったく同じように機能します。つまり、サーバー側の変更がクライアントに適用されるのは、次回のページ更新時です。

パフォーマンスに不安がある場合に、大規模で複雑な CSS ファイルを扱うときは、サーバー側で前処理する方が適していることがあります。サーバー側の前処理は、サーバー上の CSS を変更するたびに実行されます。この追加の手順は、ビルド プロセスの最後に手動で実行できます。そのためには、コマンド ラインで LESS コンパイラを使用して、LESS コードを CSS に前処理します。このコンパイラは、サーバー側の処理用にインストールする dotless NuGet パッケージに含まれています。

ただし、ASP.NET MVC 4 では、2013 年 10 月号のこのコラム「CSS のプログラミング: バンドルと縮小」(msdn.microsoft.com/magazine/dn451436) で取り上げたバンドル メカニズムに LESS フレームワークを統合できます。このようにすると、LESS ファイルを要求するたびに、LESS から CSS に変換できます。また、If-Modified-Since ヘッダーを使用してキャッシュが適切に管理されるようになります。最後に、解析と縮小を併用できます。ASP.NET MVC に LESS を統合するには、まず dotless NuGet パッケージをダウンロードしてインストールします。次に、以下のコードを BundleConfig クラスに追加します。

var lessBundle =
  new Bundle("~/myless").IncludeDirectory("~/content/less", "*.less");
lessBundle.Transforms.Add(new LessTransform());
lessBundle.Transforms.Add(new CssMinify());
bundles.Add(lessBundle);

このバンドルは、指定したフォルダーにあるすべての .less ファイルをパッケージ化します。LessTransform クラスは、LESS から CSS への変換を実行します。このクラスでは dotless API を使用して、LESS スクリプトを解析しています。LessTransform クラスのコードはかなりシンプルです。

public class LessTransform : IBundleTransform
{
  public void Process(BundleContext context, BundleResponse response)
  {
    response.Content = dotless.Core.Less.Parse(response.Content);
    response.ContentType = "text/css";
  }
}

高度なツール

CSS の前処理ツールは LESS 以外にもあります。一例を挙げれば、Syntactically Awesome Stylesheets (Sass) (sass-lang.com、英語) も有名な前処理ツールの 1 つです。結論としては、どのツールを使用するとしても、大規模な Web プログラミングでは必ず CSS の前処理ツールについて検討する必要があります。グラフィック デザイナーにも開発者にも、CSS コードを管理して整理するための高度なツールはほぼ不可欠です。高度なツールを Web プラットフォームにも統合すると、さらに優れたものになります。最後に、Visual Studio 2012 と Visual Studio 2013 では、vswebessentials.com (英語) でダウンロードできる Web Essentials を通じて LESS (や関連テクノロジ) に対する優れたサポートを利用できることに注目してください。また、Visual Studio 2012 Update 2 と先日公開された Visual Studio 2013 では、LESS エディターを利用できます。

Dino Esposito は、『Architecting Mobile Solutions for the Enterprise』(Microsoft Press、2012 年)、および近日出版予定の『Programming ASP.NET MVC 5』(Microsoft Press) の著者です。JetBrains の .NET および Android プラットフォームのテクニカル エバンジェリストでもあります。世界各国で開催される業界のイベントで頻繁に講演しており、software2cents.wordpress.com (英語) や Twitter (twitter.com/despos、英語) でソフトウェアに関するビジョンを紹介しています。

この記事のレビューに協力してくれた技術スタッフの Mads Kristensen (マイクロソフト) に心より感謝いたします。
Mads Kristensen は、マイクロソフトの Web プラットフォームとツール チームのプログラム マネージャーとして、Visual Studio の Web 開発エクスペリエンスに取り組んでいます。マイクロソフト プラットフォームでの Web アプリケーション開発に関して 10 年以上の経験があります。彼は、世界中に 800,000 人のユーザーがいる、ASP.NET プラットフォームで最も有名なブログ アプリケーションとなった BlogEngine.NET オープン ソース プロジェクトを立ち上げました。また、Kristnsen は、Web Essentials、Image Optimizer、CssCop など、複数の有名な Visual Studio 拡張機能の作成者でもあります。