Shopify Unite 2021では、マーチャントのテーマにapp block(英語)を介してアプリの組み入れが可能となる開発者向けのtheme app extensions (英語)がアナウンスされました。Theme app extensionsは、マーチャントがストアフロントであなたのアプリを操作する方法を完全に変えます。
App blockは、テーマ内にアプリを組み入れる方法も大きく変えます。ページにインラインコンテンツを挿入するアプリは、app blockによってテーマにまで拡張できます。つまり、アプリ開発者はマーチャントがテーマエディターで追加、削除、設定が可能なUIコンポーネントを構築できるのです。
マーチャントは、app blockを互換性のあるテーマセクションに追加するか、プラットフォームが提供するセクションにラップすることができます。このセクションは、全幅、固定サイズ、コンテンツと並行、またはコンテンツに内包が可能です。ということは、app blockは追加するセクションのサイズに対してレスポンシブになる必要があるわけです。
以下の例をご覧ください。カスタマーレビューアプリのapp blockは、商品詳細セクションの内側にも下側にも追加できます。下側に追加されると、このブロックは追加の余白スペースを得ることができ、星評価が右側に表示されます。一方、商品詳細セクションの内側に追加されると、レビュータイトルの横に星評価を表示させる余白がありません。そこでタイトルの下にレンダリングされるように、レイアウトをシフトすることになります。
App blockはさまざまなテーマ、テンプレート、セクションで機能する必要があるため、あなたのapp blockがこれらの多様な環境にどの程度適応できるかを検討することが重要です。
App blockはさまざまなテーマ、テンプレート、セクションで機能する必要があるため、あなたのapp blockがこれらの多様な環境にどの程度適応できるかを検討することが重要です。
今回の記事では、app blockをレスポンシブにするためのベストプラクティスについて説明します。
こちらも参考にしてください:Shopifyアプリとテーマの開発方法
ブラウザの制限
App blockをレスポンシブにするソリューションを確認する前に、ウェブデザインの標準的なソリューションと、それが特定の場面で機能しない理由を、簡単に見ておきましょう。
Mediaクエリ
レスポンシブなウェブデザインといえば、mediaクエリ(英語)が思い浮かびます。Mediaクエリは、デバイスやブラウザのサイズ、もっとも一般的なのはviewportのサイズですが、そのプロパティに応じてCSSを適用します。たとえば、mediaクエリを使ってモバイルとデスクトップとで異なるウェブサイトをレンダリングできます。
ただし、わたしたちのケースでは、viewportでUIのレスポンシブネスを指定するのではなく、コンテナのマークアップのサイズが基準になります。なぜかというと、app blockはつねにテーマが提供するセクションの内部に追加されるため、その周りの要素とコンテンツを把握できないからです。Mediaクエリを使用すると、実際には要素が密集している場面でもapp blockは十分なスペースがあると判断してしまうでしょう。
注:Mediaクエリがapp blockの開発で役に立たないというわけではありません。Mediaクエリは、UIをさまざまなデバイスに適応させてレイアウト以外の部分を含めコントロールするうえで重要です。
Containerクエリ
Containerクエリは、まさにこの問題を解決(英語)します。提案されている構文は、mediaクエリを使ったことのある開発者にはおなじみのものです。
ただし、これらは(まだ)ブラウザではサポートされていません(英語)。Chromeはこの構文をChrome Canaryの機能フラグで実装しています(chrome://flags)が、まだ提案段階(英語)です。FirefoxやSafariで採用されるかどうかは明確になっていません。つまり、現段階では標準と考えることはできず、構文は今後変更されることが考えられます。
Containerクエリへの関心が高まっているので、ソリューションはそのうち出てくると思われます。ただ、多くのブラウザで広くサポートされるまでは、暫定的なソリューションと考えるべきです。
CSSグリッドとCSS関数
エレガントなソリューションとはいえず、containerクエリと同じケースをすべて解決できるわけではありませんが、すでにブラウザにサポートされているツールを使ってコンテナレベルのレスポンシブなレイアウトを達成できます。
Mediaクエリを使わないフレキシブルなレイアウト(英語)の記事で、Dannie VintherはCSSグリッドとCSS関数を使用したソリューションを構成するコンポーネントについて説明しています。一般的なアプローチと、app blockへの適応方法を以下で見ていきたいと思います。
ソリューションとなるCSS関数がいくつかあるので、それらの詳細を確認しましょう。
min()、max()、clamp()
このソリューションの鍵は、特定の基準に応じて動的に値が変更できることです。そこで、min()、max()、clamp()が役に立ちます。
min()
名前が示唆しているように、min()は渡された値から最小値を返します。
.child {
width: min(100vw, 200px);
}
上の例では、viewportが200pxを超える場合は要素の幅が200pxとなり、viewportが200px以下の場合には要素がviewportの幅となります。
max()
max()は渡された値から最大値を返します。
.child {
width: max(50vw, 200px);
}
上の例では、viewportが400pxを超える場合は要素の幅がviewportの50%となり(400pxを超えるviewportの50%は200px以上となるため)、viewportが400px以下なら、要素は200pxとなります。
clamp()
最大値と最小値の範囲内の値を返したい場合は、上記の関数を両方組み合わせることが可能です。または、clamp()という便利な関数を使えば、最小値、優先値、最大値を取ることができます。
.child {
width: max(50vw, min(200px, 100vw)); // equivalent to clamp() below
width: clamp(50vw, 200px, 100vw);
}
上の例では、幅が200pxより小さくなることはありません。
単位
これらのCSS関数はかなり単純ですが、上記の例では単位としてvwを使っていて、これはviewportの幅に相当します。これにより、mediaクエリがすでにやっていたのと同じ問題を解決します。app blockの使用ケースでは、親要素に対しての相対値を計算したいので、幸いなことにパーセンテージを単位とすることでそれが可能となります。
.child {
width: clamp(50%, 200px, 100%);
}
CSSグリッド
上述したCSS関数を使えば、親コンテナに対する特定の値を取得できます。しかし、これらをapp blockをレイアウトするためにどのように使えばよいのでしょうか? その方法の1つが、CSSグリッド(英語)です。
CSSグリッドは、ウェブページに要素を配置する方法として一般的になってきました。以前は、Table、Float、Inline-blockなど、また比較的最近ではFlexboxが使用されていました。フレックスボックスには使用ケースがまだありますが、二次元のグリッドベースのレイアウトシステムの利点をCSSグリッドはもっているため、開発者のコントロールをより強固にできます。
CSSグリッドの機能を深く掘り下げるなら、 CSS-Tricksのグリッド完全ガイド(英語)をチェックすることをお勧めします。今回の記事で考慮すべき重要な点は、CSSグリッドはgrid containerとgrid itemという2つの要素から成り立っているということです。
- Grid container : display: gridが適用されるラッピング要素
- Grid item: grid containerの直接の子要素
この記事のためにプロパティと値についておもに検討します。CSS関数を使ってCSSグリッドをコントロールする方法を理解するために、例を見てみましょう。
CSS関数のclampが使われているのがわかりますが、ほかにもCSSグリッド特有のものがあって、わかりにくいかもしれないので1つずつ確認していきます。
- display: grid:子要素のためにCSSグリッドによる配置をおこなうことを指示します。
- grid-template-columns:グリッドの列のサイズを定義します。
- repeat: grid-template-columnsとgrid-template-rowsのプロパティの値のサイズをリピート可能にします。たとえば、最初のNを一定のサイズにして最後のものを違うサイズにできます。auto-fitは、列のサイズ設定と要素の折り返しを処理するようにブラウザに伝えます。オーバーフローせずに収まるほど幅が十分でない場合に要素が行に折り返されます。
- minmax:最小値と最大値を取り、最小値以上と最大値以下のサイズ範囲を定義します。これはCSSグリッド内でのみ有効です。1frは、各列が均等に余白を取れるように、カラム間の余白を分配することをブラウザに伝えます。
CSSグリッドのオートサイジングの詳細については、Sara Soueidanの記事「CSSグリッドのオートサイジング: auto-fillとauto-fitの違い(英語)」をご参照ください。
全体をまとめると、ブラウザに対して要素のアイテムをCSSグリッドで配置するように伝え、ブラウザがclamp()の計算をもとにカラムのブレークポイントを算出する際にauto-fitとminmax()を使用し、動的なレスポンシブグリッドを作成できるようにしています。
制限
もちろん、CSSグリッドとCSS関数がすべてのレスポンシブ問題を解決できるなら、containerクエリは不要でしょう。しかしそういうわけにもいきません。このアプローチでは、親コンテナのサイズをもとに何が変更できるかという点において制限があります。
- 特定要素の表示や非表示ができません
- サイズベースではないプロパティを変更できません
こうした制限があるとはいえ、CSSグリッドとCSS関数を使えば、どのページに追加されてもapp blockの見栄えが良くなるように十分ダイナミックな対応が可能です。
CSSでApp Blockをレスポンシブに
Theme app extensionsとapp blockは、多くの楽しみな機会をもたらします。これらを使用することでマーチャントは、app blockが追加されたストアフロントで完全なコントロールを手にします。マーチャントがapp blockをどのように使おうとするかを予測し、見た目もきれいでしっかり機能するように保証するのが開発者の務めです。Containerクエリが適切にサポートされるまでの間は、CSSグリッドとCSS関数が暫定的なソリューションを提供します。
原文:Owain Williams、Max Hoffman 翻訳:深津望