数年前、「Web Components」という言葉がよく聞かれるようになり、当時は私自身もワクワクしていたのですが、一過性のニュースのごとく、いまではまったく忘れられてしまっています。しかし、ここ最近、Web Componentsが再び注目を集めている模様。特に、Web Componentsを使ったレスポンシブWebデザインの構築がかなりシンプルにできるようになるようです。
きっと誰もが疑問に思うことといえば、次の4つでしょう。
Web Componentsは…
- 最新のWeb環境に対応可能か?
- クロスブラウザー環境に対応できるのか?
- 落とし込まれたスペース内に適応できるのか?
- コードのモジュール化はどこまでできるか?
この記事では、これら4つの質問を検証しながらお答えしたいと思います。
Web Componentsってなに?
「Web Components」は本来、HTML要素をカスタマイズ可能にするための仕様です。Web Componentsについてまったく知らない人は、この記事でも使用される基本的な用語を理解するために、こちらの記事を読んでみてください。
Web Componentsはどうして便利なの?
Web Componentsは、カラーピッカー、カルーセル、アコーディオン、タイプヘッドなど、プラウザーにもともと用意されていないUIを構築したいときに使うと便利です。具体的には、SNSボタンや購読申し込みフォーム、通知など、さまざまなサイトでよく用いられるコンポーネントがいくつかあり、それを毎回構築しなければならないときでしょう。
そんなとき、代わりにWeb ComponentsでHTMLタグを使って、例えば<color-picker></color-picker>といったように、スタイルとスクリプトをまとめてマークアップできます。AngularJSのディレクティブやReactのコンポーネントで見慣れているタグだと思いますが、Web Componentsのメリットはネイティブ実装が可能で、フレームワークに依存しないことです。
Web Componentsのよいところは、自分でUIパーツを1回構築しておけば、さまざまな場所で再利用できること。Living Styleguideのような、各コンポーネントごとに、信頼できるシングルソースのライブラリを構築したいプロジェクトにとって理想的です。何度も同じコードを書く必要がなくなり、いつでもコンポーネントにアクセスできるようになります。
また、カスタマイズしたコンポーネントをまとめて他のデベロッパーやサイトオーナーが共有できるのも、Web Componentsを使うもう1つの大きなメリットです。
基本的に、以下のようなインポートステートメントをサイトに置けるので、コンポーネント内で定義したCustom Elementを引っ張ってこられるようになります。
<link rel="import" href="http://url.com/subscribe-form.html">
これがインポートされている状態であれば、カスタムコンポーネントをいくつでも引っ張ってこられます。メルマガ登録フォームの例では、 <subscribe-form></subscribe-form>のタグをサイトの各所に置けます。もちろんこの場合、使用する独自のコンポーネントは、どの端末やスクリーンサイズにも適応可能なフレキシブルなものである必要があります。
レスポンシブ Web Componentsにしてみよう
さて、実際の例が一番、分かりやすいですよね。メルマガ登録フォームは、サイトや実装環境に特に左右されないごく一般的なUI要素なので、これを例に説明していきたいと思います。
以下が私が作ったものです。
このWeb Componentテンプレート内には、フォームの作成に必要な基本的なHTMLがあらかじめ入っています。
<ul class="form">
<li class="form__item">
<label for="name">Name:</label>
<input id="name" name="name" type="text">
</li>
<li class="form__item">
<label for="name">Email:</label>
<input id="name" name="name" type="text">
</li>
</ul>
このHTMLとCSSは、コンポーネント内にShadow DOMの詳細として隠されている状態です。これでカプセル化が可能となり、他のスタイルに影響されず、親サイトにスタイルが影響してしまうことを心配せずに、labelとinput セレクターを自由に使えるようになります。
簡潔に説明するために、レイアウトに使われているスタイル以外は記載していませんが、ここではfloat:eftとdisplay: tableのコンビネーションを使ってレイアウト幅が範囲外に描画されないようにしています。
.form__item {
display: table;
float: left;
width: 50%;
}
label {
display: table-cell;
width: auto;
}
input {
display: table-cell;
width: 100%;
}
例として、HTMLインポートを使ってSitePointのサイトに引っ張ってきてみましょう。
これでベースは完了です。それではレスポンシブのテクニックを見ていきましょう。
メディアクエリ
レスポンシブWeb Componentでは、典型的なコーディング方法がまだまだ健在です。ブレイクポイントをあらかじめ設定しておきたければ、コンポーネント化する際にテンプレート内で適用しておけますし、利用者に設定させる場合は、コンテナーにフック(例えばclass属性を使うなどして)することもできます。この場合、floatはnoneを指定し、widthを100%にするだけです。
@media (max-width: 30em) {
.form__item {
float: none;
width: 100%;
}
}
しかしメディアクエリの場合、これだけではまだ不十分です。
このメルマガ登録フォームとスタイルは十分機能していますが、もしサイトの作成者がフォームをサイドバーに入れたい、なんてことを言い出したら...? そうすると、このフォームはいきなりぎゅっと押しつぶされてしまって、使い勝手が良いとは言えませんよね。
なぜならこのコンポーネントはレスポンシブに対応するように設定されていないからなのです。
Web Componentのメリットは、どこにでも落とし込めてちゃんと機能するってことですよね? ということは、これは明らかにダメです。しかし、以下のテクニックを使って、独自のコンポーネントをレスポンシブに対応するように変えることができます。
flexboxのトリック
Web Componentsを使う場合、最新のブラウザーに対応はしても、IE9への対応は考えていないでしょう。最新のブラウザーをレスポンシブWeb Componentsが使えるようにするには、flexboxのトリックがぴったりです。これは特にJavaScriptを追加する必要がないので、よく使う方法でもあります。
コードのサンプルは、前例と同じく、レイアウトに関わる部分のみです。
.form {
display: flex;
flex-wrap: wrap;
}
.form__item {
align-items: center;
display: flex;
flex: 1 0 320px;
flex-wrap: wrap;
max-width: 100%;
}
label {
flex: 1 0 90px;
}
input {
flex: 1 0 230px;
width: 100%;
}
formでdisplay: flex; flex-wrap: wrapを設定することで、.form__itemの要素が並んで表示されます。しかしこれだけで終わりではありません。flexコンテナーが小さくなりすぎた場合にもうまく積み重なるようにもう少し調整を加えます。
.form__itemに、 flex: 1 0 320px;のflexのショートハンドを使っており、flexプロパティを「flex-growを1、flex-shrinkを0、flex-basisを320ピクセル」と変換します。設定したflex-basis(320px)よりも小さくならないように、flex-wrapを設定することで、残りのスペースを埋めるように設定されました。似たような設定をlabelやinput要素に適用することもあります。これらのflex総計は320となり、flexコンテナーのサイズが小さい場合も思い通りに動作するはずです。
さて、この調整がどのように反映されたか、SitePointのサイドバーを見てみましょう:
いいですね! これ以外にも方法はあります。
Elementクエリ
Elementクエリの意義は、コンポーネントレベルでメディアクエリの関数をエミュレートできることです。レスポンシブWeb Componentsには便利な関数ですね。例えば、CSSで下記のようなコードを書けてしまうなんて、想像できます?
.form__item[max-width~="30em"] {
/* Styles here */
}
はい、Elementクエリなら、できてしまうんです。
ただし、無限ループを引き起こす可能性がブラウザー側で懸念されているため、このテクニックはネイティブで有効でないのが難点です。でも、これが使えるプラグインを書いた頭の良い人たちがいるのでご安心を。
下記の例では、Marc J Schmidt氏が書いた、かなりいい感じに実装できる CSS Element クエリを使っています。他にも似たようなプロジェクトもあるので参照してください。
これらに共通するのは、JavaScriptでコンポーネントの幅を認識して、CSSでスタイル設定できるように属性を修正するということです。イベント系またはリサイズ系があります。
.form__item[max-width~="30em"] {
float: none;
width: 100%;
}
このようなプラグインと上のようなコードを使えば、サイドバー内のFlexboxとまったく同じ結果になります。
属性
レスポンシブWeb Componentsのもう1つの特徴が、属性経由のAPIです。例えば、「レイアウト」属性の中に「small」と「large」のサイズを設定したとしましょう。すると下記のようなコンポーネントが使えるようになります:
<subscribe-form layout="small"></subscribe-form>
これで「small」のスタイルの実装がトリガーされます。
Shadow DOMの場合、 :hostとして内容要素が記載されているはずです。例えば次のようにです。
:host {
display: block;
}
:host([layout = "small"]) .form__item {
float: none;
width: 100%;
}
:host([layout = "large"]) .form__item {
display: table;
float: left;
width: 50%;
}
サイズ変換させる簡単なJavaScriptを書くだけで、このコンポーネントの実装が可能となります。モジュール内に新たなJavaScriptをロードする代わりに、フックの仕方を利用者側が設定できるようになります。このように、新たに作った独自のコンポーネントは、時間が経っても通用するのです。
それでは実際にやってみましょう!
もしFlexboxバージョンも見てみたい、という場合は、Chromeで以下を試してみてください:
- HTMLインポートを外部サイトから動作可能にするために、 CORS Chrome extensionをインストールして起動
- エクステンションを起動させるために、ページをリロードするか、新規タブまたはウィンドウでどこかのサイトを開く
- Chromeデベロッパーツールで、次のタグを<head>に追加
<link rel="import" href="http://wsh.webfactional.com/web_components/components/ cm-subscribe-form--flex.html">
- 最後に、次のタグを追加
<cm-subscribe-form-flex></cm-subscribe-form-flex>
<body>のどこかに、私のメルマガ登録フォームが表示されるはずです。
まとめ
今回紹介したアプローチは、実際はどのUIの開発にも使えますが、再利用可能なこと、他の開発者との共有のしやすさから、特にレスポンシブWeb Componentsとの相性が抜群です。
もしまだWeb Componentsを使っていないのなら、ぜひ試してみてください。完全な仕様は現状ではChromeとOperaでしかネイティブサポートされてはいませんが、近い将来他のブラウザーでもサポートされるでしょう。
(原文:How You Can Use Responsive Web Components Today)
[翻訳:Eri Noda]
[編集:Livit]