
背景
2013年、私はたくさんのデータ表示を必要とするレスポンシブWebアプリのフロントエンド開発をしていました。それまでに@mediaクエリを使ってたくさんのレスポンシブデザインを制作していましたが、1つのレイアウトから別のレイアウトにコンポーネントを再利用しようとして、レスポンシブブレークポイントをブラウザーの幅ではなく要素の幅に対応して設定できればと考えました。
当時のCSSでは実現できなかったので、テンプレートからテンプレートへとたくさんのスタイルをコピー&ペーストし、ブレークポイントをほぼぴったり合うように変更しました。ツールやJavaScriptプラグインを中心に、プロセスを自動化したり、複製したコードを出力したりするのに役立つ既存の対処法を探しましたが、解決策として満足のいくものは見当たりませんでした。
「Less」については知っていました。LessとはCSSのプリプロセッサーで、標準CSSに組み込まれていない変数や関数などの追加機能を搭載したオリジナルのCSSを作成できます。非標準CSSをブラウザーに読み込む小さなJavaScriptプラグインをWebサイトに追加すると、驚いたことにオリジナルの非標準コードはブラウザーが理解できるスタイルに変換されるのです。同様にCSSを拡張して課題を解決する方法を考えるようになりました。
プラグインの誕生
そうこうしているうちにMaximeという優秀でクリエイティブなコーダーに出会いました。私はMaximeの実績に夢中になりました。またCSSとJavaScriptに関する彼の知識と理解は私のそれをはるかに超えていました。ある日、MaximeにCSSの課題について、次のようなメッセージを送りました。
以下のことができるCSSスタイルの記述方法を求めています。
- 要素の現在の幅を基準にして異なるスタイルを指定する
- 要素の現在の高さを基準にして異なるスタイルを指定する
- 親要素内で要素の縦方向の中央揃えを常に維持する
- 親要素内で要素の横方向の中央揃えを常に維持する
- 要素のテキスト長を基準にして異なるスタイルを指定する
- 要素に含まれる子要素の数を基準にして異なるスタイルを指定する
- さらなるメリットとして<セレクタを使ってDOMをナビゲートできる
このようなライブラリがあれば、本当に堅牢で崩れにくいレイアウトをデザインできるに違いありません。@elementクエリが必要なのです!
CSSを書く人になじみがあり、しかもJavaScriptが読み取り・実行できる方法でこうしたスタイルを記述できるでしょうか?
おそらく.jssファイルや<script type="text/jss">といったテキスト、つまりCSSブロックを記述でき、それをJavaScriptが読み取れてページに適用できる算出スタイルを格納するような専用の@elementクエリでラップできるようなテキストを、JavaScriptはパースできるのでしょうか?
@element('.widget-box', min-height: 500px) { .widget-box { background: red; } .widget-box a { font-size: 18pt; } }
または、次のようなことです。
@element('#username', min-length: 20) { #username { font-size: 8pt; } #username < label { border: 1px solid red; } }
実際に上のコードを使い勝手の良いものにするには、すでにCSSを理解しているもののJavaScriptは分からないという人が簡単に習得できるようにする必要があります。サイトにJavaScriptライブラリーを追加してカスタムスタイルを記述し、カスタムJavaScriptを一切必要とせずに動くようにできると良いでしょう。だとすると、私はこれがプラグインというよりポリフィルではないかと考えます。
こうしたことは可能でしょうか?
Tommy、2014年12月5日
どのような返事をもらえるか心配でした。私はすでにプラグインをいくつか作っていましたが、あまりうまくいきませんでした。JavaScriptの初心者だったのでオリジナルで作れるものにはかなり限界があり、作ったソリューションはどれも、かえって作業を複雑にしてしまいました。ソリューションを本当に価値あるものにするには、使うと全体の作業量が減り開発が楽になるようにしなればなりません。制約を加えるのではなく、取り除かなければならないのです!
さっそくMaximeから返事が来ました。
すべての質問に対する答えは「はい」です。実現できます。:)
解決策は1つではなく3つあります。
CSS機能を拡張して、現在、メディアクエリではできないことを実現できます。つまり要素のサイズや要素のテキストコンテンツの長さを基準にしてある種のスタイルを適用するのです。
CSSのセレクターを拡張して親セレクターを追加できます。
あるものにvertical align(縦方向の位置揃え)を適用する方法を別のものに追加することによって、flowに関連した標準CSSプロパティを拡張できます。とても高いハードルではありますがCSSを使ったこの3つの秘策があります。
Maxime、2014年12月5日
それから数週間カナダ、フランス、アメリカ間でメールを活発にやり取りし、Maximeと私はこの新しい構文をどのようにするか考察しました。まだ世に出ていなかった言語でコードを書いてシェアし、潜在的な問題や対処法について話し合い、ついにMaximeは私の構想と必要に対応したJavaScriptプラグイン「EQCSS」の初版を作り上げました。
まもなく、作業中のWebサイトにEQCSSプラグインを使えるようになり、2015年1月には本番サイトで実際に使いました。その後、数カ月にわたって実験を続け、新機能を追加し、対応とパフォーマンスを改善しました。プラグインを一から作成したので、トラブルシューティング、修復、メンテナンス、独自の新機能の追加さえもできるほどJavaScriptに詳しくなりました。
プラグインを構築した理由
こうしたソリューションの作成にとても多くの時間と労力を費やした理由を振り返ると、2つの動機があったように思います。ひとつの動機として、とにかく、作業で毎日直面する課題の解決策を必要としていました。すぐに使えたものには、使い始めたその日から時間の節約になるものもありました。
また、プラットホームとしてのWebにかなりの柔軟性があることに気づきました。つまり、Webにおける基本的な言語の1つ(CSS)をオリジナルで改変・拡張して新機能を追加することは可能かどうか、どれほどの道のりで達成できるのか、というのがもうひとつの動機です。
当初着手するにはこうした理由があれば十分でしたが、目指していたソリューションがほかのソリューションに勝るとも劣らないものとなったいま、さらに動機が加わりました。このソリューションを改良して同様の課題を解決するさらに標準化した手法を提供し、多くの人がメリットを得られるようにできるかどうかです。
プラグイン作成における課題
プロジェクトではたくさんの課題に直面しました。構文そのもの、プラグインの記述、さまざまなブラウザーの機能変更時に対応の維持などの技術的な課題、ユーザーがコンセプトを理解してプラグインが提供する最大のメリットを得られるようにするにはどうすればよいかなど人を対象にした課題です。
構文における課題
構文で直面した課題に、どのように構文をすべて単一の言語、つまりCSSに限定できるのかということがありました。CSSで書かれたコードを動かすためにユーザーがHTMLマークアップになにか余分に追加したり、はじめるにあたってユーザーがカスタムJavaScriptをオリジナルで書いたりする必要は避けたいと思いました。
別の課題は、構文の設計に関してすぐに対応が必要なユースケースを扱うのに十分な表現力を持ちながらも、ユーザーが必要に応じて追加機能を記述できるよう十分な柔軟性を持たせることでした。この優れた柔軟性は新機能のテストと追加の際のメリットとなりました。なぜならプロトタイプの作成時にこのプラグインを使ってカスタムコードを書いていきますが、プラグイン拡張に必要となる形に変換されるからです。プラグイン自体を新機能のプロトタイプに使用でき、さらに柔軟性を加えたことで新機能追加のスピードが向上しました。
言語の新機能を開発する場合、CSSで私たちがしたのと同様、将来名前が似ているものの自分が実装したものとは異なる働きをする機能がその言語に追加されることのないように、将来性を考えた(future-proof)構文設計をすることが大切です。開発したプラグインではCSSからカスタム構文を読み込めます。さらには、ブラウザーがCSSとして読み取らないような別のスクリプトタイプとしてプラグインから直接ロードもできます。このカスタムスクリプトタイプのおかげで、作成した構文の新しい用語を類似の用語を持つ別の言語と同じコードベース内でも衝突することなく共存できました。
プラグインにおける課題
プラグインを作成(以前これほどのスケールのものを作成したことはなかった)した目的の1つは、ファイルサイズを適度に小さく保ち、ソースコードを平易にしてだれでも必要に応じて読み取り、編集し、拡張できる状態にすることでした。また、追加機能がInternet Explorer 8で動くようにすることも重視しました。IE8のためだけに必要なコード量はコードベース全体の大きな部分を占めてしまいましたが、IE8用のコードを専用のファイルに分離できるようにプラグインを構築しました。この追加ファイルのインクルードが必要なのはIE8対応が求められるプロジェクトだけで、それ以外のプロジェクトでは問題なくこのファイルを省けます。
ブラウザーにおける課題
Webブラウザーで動く必要のあるプラグインを設計していると、Webブラウザーを動的なターゲットとして見るようになります。このプラグインを作成した当初Chrome、Safari、Firefox、Internet Explorerでテストしましたが、はじめInternet Explorerはバージョンが古く、プラグインにとても厳しい制限がありました。しかし、このプラグインが製品化されて1年経った2016年初めに、Firefoxの新しいバージョンでこのプラグインを使ったページが重大なパフォーマンスの問題を抱えているというバグレポートを受け取りました。コードはなにも変更していませんでしたが、このバグに関してFirefoxのさまざまなバージョンを調査してみると、Firefoxがページのスクロールイベントを認識する方法になにか変化があったらしく、プラグインで必要以上に頻繁に再計算がトリガーされていました。
Firefoxのために提案された解決策は、絞り込み(debounce)メカニズム、つまり再計算がリクエストされる頻度の上限を設定できるコードをプラグインに追加することでした。Firefoxではこの方法で問題を解決できそうでしたが、そのほかのブラウザーで影響がでるかもしれないという潜在的な不測の課題を抱えることになりました。さらに悪いことに、Firefoxのプレリリース版でソリューションをテストしてはいたものの、この問題を解決するにはFirefoxのリリースから数カ月経ってからになりそうでした。プラグインを使っている人たちがいること、 しかも私たちがパッチを当てない限り、世界中のFirefoxのユーザーが幾月もパフォーマンスの低下を経験することを考えるとストレスを感じました。たくさんのテストを重ねてついに絞り込みメカニズムを追加するパッチをリリースし、Firefoxユーザーに影響するバグが修復されただけでなくほかのブラウザーでもパフォーマンスが向上しました。
モジュールにおける課題
当初、私たちはポリフィル(またはシム)のように動作するプラグインを作成しました。ブラウザーで直接実行されるようにプラグインを設計したのでCDNにホストしやすくなりました。ほどなくして、JavaScriptモジュールを使ってプロジェクトを作成していて同様のパッケージ版のプラグインを求めるWebpackのユーザーからリクエストが届き始めました。幸い、プラグインをモジュールに変換するUMDモジュールテンプレートのコードで既存のプラグインをラップできました。現在このプラグインはWebpackやBrowserifyなどのモジュールローダーでロードできます。以前と同様、ブラウザーで直接ファイルにリンクするなどモジュールローダー以外の方法でプラグインをロードする場合、プラグインは以前とまったく同様にグローバルオブジェクト(ブラウザー)にアタッチされて通常どおりに動きます。
ドキュメントにおける課題
新しいプラグインを作成していて課題となった最後の分野は、この新しいコンセプトがどのように働き、ユーザーがプロジェクトでどのようなメリットを得られるかを説明できる用語を探す(または作成する)ことでした。時間をかけて多くの人と話し、仕様書、ドキュメント、たくさんの記事を執筆していくうちにこの隔たりは埋まってきています。それでもプラグインで新しい技術やコンセプトを扱うときはいつも同様のコミュニケーションギャップに直面することでしょう。
あなたもやってみませんか?
プラグイン作成を経験して、同様のことをぜひまたやりたいという気持ちがこれまでになく強くなっています。とてもできないと思っていたことを実現すると奇跡が起きたような気持ちになるだけでなく、同様の課題に直面して、どのように解決できるかに、ついに気づいた瞬間の開発者たちの反応を体感するのはとても価値あることです。
コミュニティでソリューションを共有すると3つのメリットを分かち合えます。
- 多くの人が自分のコードを読んでくれて、そのテクニックを作業に使ってメリットを得る
- この先、いつでもスタートするための標準化されたリファレンスを持っていることでメリットを得る
- ほかの人が自分では気付いていない機能を提案してくれたり、エッジケースについてレポートしてくれたりするので、情報を活用してソリューションを改善できる
デメリットはありません。ソリューションができたら、それが私の場合のように言語機能であれば、新機能の仕様書を執筆して言語の標準に仕様を含めてもらうよう提案するプロセスを開始することで先に進めます。
プラグインの作成後、さらに新しいアイデアやコンセプトを探すような方法でソリューションにテコ入れしたり、作ったプラグインを足がかりとしてさらにプラグインを実験したりできます。主にエレメントクエリでの課題を解決する目的でプラグインを作成して以来、EQCSSの強化された能力や柔軟性を使ってほかの領域でもCSSソリューションの研究や実験をしてきました。アスペクト比など(いまのところCSSに対応するプロパティが存在しないもの)に関することやCSSの属性セレクタの拡張方法をはじめ、多くのアイデアの実験をしてきました。
こうした実験を通して新しいプラグインも作成中で、うまくいけばそれらのプラグインでもさらに多くのアイデアを深めたり発掘したりできます。
唯一残念なこと
この革新的な経験をしてきて、私たちが唯一苦しんだことは、最新のブラウザーで動作させた機能すべてをIE8でも動かすことでした。それはIE8リリース以来ずっと実現可能だったことにもかかわらず、2人のオープンソース開発者が協力してプラグインを作成するまでに数週間かかりました。
このプロセス全体で残念に思っていることを1つ挙げるならば、この(IE8で実装する)アイデアをもっと早く思いつかなかったことです。もしだれかが数年前に苦労をしてプラグインを作っていれば、幾年も同様の方法でWebサイトを構築できていたことでしょう。
展望
さて、いまはどうでしょうか。すでにどのような課題があり、さほど手間をかけずに実現できるもののまだ世に出ていないだけというソリューションがあるでしょうか。なにかソリューションについてのアイデアがあるなら、それを深めてすぐにでも作成に取りかかる意義はあります。
この経験をしてWeb開発に対する全体的な見方が変わりました。まだ世に出ていないものを寝る直前まで作って、ワクワクして朝ベッドから飛び起きるような感覚に変わったのです。
(原文:How We Built EQCSS & Why You Should Try Building Your Own Polyfills Too)
[翻訳:新岡祐佳子/編集:Livit]
