TwitterのUIデザインで見つけたCSSのテクニックのまとめ
Post on:2020年5月21日
TwitterのUIデザインで見つけたCSSのテクニックを解説した記事を紹介します。
さまざまなコンテンツが入る可能性のあるコンポーネント、コンテンツが長い・短い場合、アイテムの数などを想定した興味深いテクニックが解説されています。
CSS Findings From Twitter Design
by Ahmad Shadeed
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
- はじめに
- アバター画像のアスペクト比
- CSS calc()関数の謎の使い方
- CSSの背景とHTMLの画像の混在
- スタイルのリセット
- CSSのposition: sticky;
- スペース要素
- ツイートコンテンツの追加方法
- スペースにハードコードされた値を使用する
- ナビゲーションリンクの幅
- 念のためのマージン
- ビューポートの高さが小さい場合のモーダル
はじめに
今回は、TwitterのCSSが気になって調べてみました。前回のFacebookの新しいUIデザインで見つけたCSSのテクニックのまとめを読んでいれば、似たような構成になると思います。
Twitterはほぼ1年ほど前に、新しいUIデザインをリリースしました。かっこいいところもあれば、奇妙なとこともあるので、その点についてはスルーしています。
では、始めてみましょう!
アバター画像のアスペクト比
CSSのアスペクト比を利用したプロフィールページのアバター画像の実装が興味深いことに気がつきました。
プロフィールページのアバター画像
アバター画像の実装方法は、以下のHTMLとCSSをご覧ください。
1 2 3 4 |
<a href="#" class="avatar"> <div class="avatar-aspect-ratio"></div> <img alt="" src="me.jpg"> </a> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
.avatar { position: relative; width: 25%; display: block; } .avatar-aspect-ratio { width: 100%; padding-bottom: 100%; } .avatar img { position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%; } |
CSSのアスペクト比のテクニックは、要素に垂直方向のパディング(padding-bottomまたはpadding-top)がある場合、要素の幅に依存するため機能します。
次の例を考えてみましょう。
1 2 3 4 |
.element { width: 250px; padding-bottom: 100%; } |
padding-bottom: 100%;の計算値は250pxになるので、これは完全な正方形になることを意味します。Twitterチームも同じテクニックを使用していますが、<img>要素を親要素に対して絶対位置で配置しています。なぜだと思いますか?
それには理由があります。
絶対位置を使用しない場合
画像の配置が絶対位置でないと、上記のモックアップのようになってしまいます。画像は幅と高さを100%にして配置する必要があります。そうすることで、ラッパーのサイズに合わせたサイズにすることができます。
核となる実装のコンセプトを説明したので、Twitterの実装に戻りましょう。
width: 25%;はラッパーの幅(私のスクリーンでは600px)に基づいています。なぜ彼らはこのテクニックを採用したのでしょう?
CSSをさらに掘り下げてみると、同じコンポーネントがプロフィール編集のモーダルにも使用されていることに気がつきました。ただし、これを追加したので、少し小さくなっています。
1 2 3 |
.avatar { max-width: 8rem; /* 150pxではなく112px(25%) */ } |
widthプロパティのみで画像のサイズを制御するというアイデアはとても気に入りました。下記の動画で、widthの値を変更するだけで画像もアスペクト比を維持したまま変更されるのをご覧ください。
marginの値に%を使用
アバターにはもう一つ、面白いテクニックが使用されています。
アバター画像がカバー写真と重なるように、marginの値に負の%を使用しています。
1 2 3 |
.avatar { margin-top: -18%; } |
ただし、プロフィール編集のモーダルではマージンはmargin-top: -3rem;になっています。モーダルのマージンは最大幅としても使用されているrem単位であることに注意してください。
CSS calc()関数の謎の使い方
気がついたのですが、いくつかのボタンに以下のCSSがありました。
1 2 3 |
.button { min-width: calc(45.08px) } |
calc()の中に値が1つだけというのは、意味があるのでしょうか? これには特にメリットがなく、45.08という数字が45に四捨五入される訳でもありません。
45.08pxというボタンの幅を非常に小さいだけです。アラビア語のようなRTL言語では、さらに小さく見えます。
calc()関数を使用したボタン
calcはインラインCSSとして追加されているので、これはおそらくReactを使用しての動的なコードであると予想されます。
CSSの背景とHTMLの画像の混在
CSSの背景とHTMLの画像が混在していることに複数の場所で気がつきました。
以下の例をご覧ください。
1 2 |
<div style="background-image: url(me.jpg);"></div> <img alt="" src="me.jpg"> |
これはユーザープロフィールやメディアグリッドのコンポーネントにもあるコードです。興味深いのは、<img>の不透明度が0になっていることです。アクティブな画像はbackground-imageで表示されており、画像が歪まないようにbackground-size: cover;も定義されています。
<img>で実装された画像を表示して、CSSの背景を非表示してみたところ、<img>要素を表示してもCSSの背景は非表示になってしまいました。
下記はメディアグリッドで比較したものです。
左: CSSの背景画像、右: HTMLの画像
HTMLの画像の方は歪んで見えます!
画像の歪みを防止するためにobject-fit: cover;を使用しなかった理由が分かりません。2つの画像がある理由を聞いてみたいと思います。
スタイルのリセット
CSSを調べていたら、<div>要素に使用されているCSSのクラスを多用していることに気がつきました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
.css-1dbjc4n { align-items: stretch; border: 0 solid black; box-sizing: border-box; display: flex; flex-basis: auto; flex-direction: column; flex-shrink: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; min-height: 0px; min-width: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; position: relative; z-index: 0; } |
このCSSは、文字通りページ上のすべての<div>要素に使用されています。なぜしょう? CSSのリセットで十分ではないでしょうか?
このCSSには例えば、min-width: 0;のようにリセットの一部にしてもOKというものがあるかもしれません。CSS Flexboxと何らかの関係もあります。しかし、paddingやmarginやborderはどうでしょう? そもそも<div>要素はこれらのプロパティがないのに、なぜリセットが必要なのでしょうか?
Flexboxとmin-width: 0;
min-widthのデフォルト値はautoで、これは通常0に計算されます。要素がFlexアイテムである場合、min-widthの値はそのコンテンツのサイズに等しくなります。コンテンツがラッパーよりも大きい場合、そのビヘイビアを許可するとレイアウトが崩れる可能性があります。
この問題を回避するためには、Flex子アイテムのmin-widthをリセットする必要があります。
コンテンツが長い場合
上記のモックアップは、コンテンツが長すぎる場合の例です。テキストがラッパーからはみ出していることに注目してください。
min-width: 0;を加えると、下記のようになります。
min-width: 0;を追加
CSSのposition: sticky;
Twitterの右サイドバーに、position: sticky;が使用されていることに気がつきました。中でも興味深いことは、スクロールしたときのbottomとtopの値です。
デフォルトは、下記の通りです。
1 2 3 4 5 |
.sidebar { position: sticky; width: 350px; bottom: -470.5px; } |
下にスクロールすると、bottom属性がtop: -480.5px;に置き換えられます。これはユーザーがサイドバーの最後までスクロールして、次にtopを追加するのが良い理由だと思います。
スペース要素
Facebookの記事と同様に、Twitterのチームも複数の場所でスペーサー要素を使用しており、いずれもflex-basisプロパティで幅を定義したFlexアイテムとなっています。
Twitterに使用されているスペーサー
上はflex-grow: 1;をスペーサーとして使用しており、下は固定ピクセル幅を使用しています。
ツイートコンテンツの追加方法
ツイートコンテンツ
上記は私がツイートしたものです。一見すると、<p>や<span>要素の中にテキストがあるように思うかもしれません。しかし、各絵文字は<span>要素で実装されており、2つの絵文字の間にテキストがある場合は、<span>要素として追加されることが分かりました。
ツイートコンテンツの構造
絵文字は<div>を持つ<span>で、<div>には2つの画像が含まれています。うち1つはCSSの背景で、もう1つはHTMLの画像です。
最初のテキストは<span>で内包されているため、他の兄弟との間には垂直方向のスペースが必要です。このスペースを確保するために、2番目のテキストの先頭に新しい行を追加しています。
スペースにハードコードされた値を使用する
Twitterの戻るボタン
Twitterで検索した時やプロフィールを開いた時に、戻るボタンが表示されます。このボタンはmargin-left: -4px;のマージンでオフセットされますが、その計算方法に注目してください。
1 2 3 |
.back-button { margin-left: calc(5px + (-1 * (39px - 1.5em)) / 2); } |
上記のスペースは、-4pxと計算されます。なぜ、明示的に-4pxを使用しないのでしょう? calc()で計算するより優れていると思うのですが、、、
ナビゲーションリンクの幅
ナビゲーションリンク
Twitterのナビゲーションリンクを初めて使用した時から、それぞれの幅がコンテンツと同じになっていることに気がつきました。なぜアイコンとラベルをナビゲーションアイテムの幅いっぱいにしないのでしょうか?
私が気になったのは、ナビゲーションの各アイテムにflex-direction: column;を使用していることです。なぜでしょう? 子アイテムは1つしかない場合、本当に必要ですか?
念のためのマージン
私はこれを「念のためのマージン(Just In Case)」と呼んでいます。これはデザイン上の余計なビヘイビアを防ぐために追加されたマージンのことです。Twitterでは2回ほど使用されていることに気がつきました。
念のためマージン
コンテンツが長い場合、次のことに注意してください。
1. テキストは省略される。
2. 要素間にマージンがある。
マージンは、他の要素がすべてのスペースを占有するのを防ぐための停止点として機能します。早めに考慮することで、予期せぬ問題を回避することができます。常に長いコンテンツでテストsるうようにしましょう。
ビューポートの高さが小さい場合のモーダル
プロフィール編集のモーダルを確認しようとしたところ、ある高さになるとモーダルの保存ボタンにアクセスできなくなってしまいました。
ビューポートの高さが小さいと、ボタンにアクセスできない
モーダルはスクロールできないため、保存ボタンにアクセスできません。ただし、表示を制御するモーダルの場合は、高さは動的になっています。
表示を制御するモーダルの場合
表示モーダルがスクロール可能であるのに、編集モーダルはスクロールできないのはなぜですか? どちらがより重要なのでしょうか。私にとって重要なのは、プロフィール編集です。
以上です。コメントや提案があれば、@shadeed9までお願いします。
sponsors