Open5

Every Layoutを読む

KawabataKawabata

2-01 Stack

.stack > * + * {
    margin-top: 1.5rem;
}

隣接兄弟セレクター(+)
参考: https://developer.mozilla.org/en-US/docs/Web/CSS/Next-sibling_combinator

小要素の一番最初に隣接する全ての兄弟セレクタに margin を入れてる。

フクロウの顔に見えるからフクロウセレクタと呼ばれてるらしい。

孫要素、ひ孫要素、全ての要素に再帰的に挿入したい場合

.stack  * + * {
  margin-top: 3rem;
}

Stackが作用するのは垂直方向のマージンのみとし、その責務を超えないようにすること
Stack componentでは、space propによってスペースの値を定義する。

css変数を用いて例外を上書きする。

.stack  * + * {
  margin-top: var(--space, 1.5em)
}

.stack-exception {
  --space: 3rem;
}

https://developer.mozilla.org/ja/docs/Web/CSS/:only-child

only-childで小要素が一つだった時のスタイルを設定できる。

gapがない(古いブラウザで対応していない)のでこういう書き方になってそう ↓

.stack {
  /* flex context */
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

.stack > * + * {
  margin-top: var(--space, 1.5em);
}
KawabataKawabata

2-02. Box

マージンはコンテキストに基づいて適用されるべき
幅と高さは、外因的な値(flex-basisとflex-grow, flex-shrink の組み合わせにより計算される幅など)、Boxのコンテンツから自然に導かれるべき。

入るものがなければBoxは不要。入るものがあればBoxは最適なのは、過不足のないぴったりのスペースを持ったボックス。

パディング: Box自体のスタイル設定のオプションにすべき。padding-bottomm などあるけど、 全てのへんに適用するpaddingのみを対応すれば良い。一部のpaddingのみを調整したいという場合もあるが大抵はそれはpaddingではなく、merginでは?

.box {
padding: var(--s1);
}

二つの異なる色のboxの例

.box {
  --color-light: #eee;
  --color-dark: #222;
  color: var(--color-dark);
  background-color: var(--color-light);
  padding: var(--s1, 1rem);
}

.box * {
  color: inherit;
}

.box.invert {
  color: var(--color-light);
  background-color: var(--color-dark);
}

filterを使って反転を再現することもできる(gray系の色)

.box.invert {
    filter: invert(100%);
}

ハイコントラストテーマが有効になっている場合は、boxが見づらいので outlineを入れている。outline-offsetを入れることで、borderのようにboxの境界内に移動するようにする。
https://i-center-shizuoka.jp/easier-to-read/high-contrast-mode-windows-macos/

.box {
  --color-light: #eee;
  --color-dark: #222;
  color: var(--color-dark);
  background-color: var(--color-light);
  padding: var(--s1, 1rem);
  outline: 0.125rem solid transparent;
  outline-offset: -0.125rem;
}

実装例

.box {
  /* paddingに moduler scaleの最初の値を設定、定義してないためデフォ値を入れてる */
  padding: var(--s1, 1rem);
  /* --border-thin変数が指定されていることを想定 */
  border: var(--border-thin) solid;
  /* 常に透明のアウトラインを適用することでハイコントラストモードに備える */
    outline: 0.125rem solid transparent;
  outline-offset: -0.125rem;
  /* 明るい色と暗い色の変数 */
  --color-light: #eee;
  --color-dark: #222;
  color: var(--color-dark);
  background-color: var(--color-light);
}

.box * {
  /* 親要素から色を継承させて、次の宣言ブロックで反転されるようにする */
  color: inherit;
}

.box.invert {
  /* 色の変数が反転される */
  color: var(--color-light);
  background-color: var(--color-dark);
}
KawabataKawabata

2-03. Center

中央揃えをする最も簡単な方法としてmarginがある。
よくあるやり方として以下のようなやり方がある

.center {
    margin: 0 auto;
}

メリット:
数バイトの文字の節約になる

デメリット:
margin-top, margin-bottom があったときに打ち消されてしまう

これを踏まえると

.center {
    margin-left: auto;
    margin-right: auto;
    max-witdh: 60ch;
}

の方が好き。 stackがgapを用いてない設計になっているので余計そうなっている。

コラム
max-width は基本的にch単位で設定するべきです。適切ななカラム幅を基準に考えるためです。(todo 調べる)

60chのmax-widthを維持しつつ、両端には少なくとも --s (1rem) の空白を確保する例。content-boxにすることで、60vhの計算を簡単にしてる。

.center {
  box-sizing: content-box;
  max-width: 60vh;
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--s1, 1rem);
  padding-right: var(--s1, 1rem);
}

flexを用いた内在的な中央揃え

.center {
  box-sizing: content-box;
  max-width: 60vh;
  margin-left: auto;
  margin-right: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
}

実装例

.center {
  /* paddingを幅の計算から除外 */
  box-sizing: content-box;
  /* 最大はばは最大カラム幅に */
  max-width: 60vh;
  /* 水平方向のマージンのみに作用 */
  margin-left: auto;
  margin-right: auto;
  /* 水平方向に最小限のスペースを適用 */
  padding-left: var(--s1, 1rem);
  padding-right: var(--s1, 1rem);
  /* 内在的な中央揃え */
  display: flex;
  flex-direction: column;
  align-items: center;
}
KawabataKawabata

2-04: Cluster

サイズや形が不確定な要素が集まったグループを処理するときにこうした行内の単語のように流動的に展開したいことがあります。

これを実装する方法として

.element {
    display: inline-block;
}

inline-blockを使う方法があります。

しかし、スペース文字で引き離されてしまいます。

<ul>
  <li class="list-item">HTML</li>
  <li class="list-item">CSS</li>
  <li class="list-item">Javascript</li>
</ul>
.list-item {
  display: inline-block;
}

わずかに間開く。これを改善するには親要素に font-size を 0にする必要がある。

.list {
  font-size: 0;
}

.list-item {
  font-size: 1rem;
  display: inline-block;
}

折り返されると変なスペースが生まれてしまう...

解決策

.cluster {
 display: flex;
 flex-wrap: wrap;
 gap: var(--space, 1rem);
}

gapが対応されていない場合は注意が必要と書いてるけど、まあええでしょ。

グレースフルグラデーションという考え方もあるらしい
https://developer.mozilla.org/ja/docs/Glossary/Graceful_degradation

位置揃え、クラスターでは justify-contentにどの値を設定しても構わない。cluster componentは、長さが異なり。折り返しが発生しやすい要素に適しています。

実装例:

.cluster {
  /* flexbox context を設定 */
  display: flex;
  /* 折り返しを有効化 */
  flex-wrap: wrap;
  /* スペースあるいはgapを設定 */
  gap: var(--space, 1rem);
  /* 主軸の位置揃え */
  justify-content: center;
  /* 交差軸の位置揃え */
  align-items: center;
}
KawabataKawabata

2-05 Sidebar

メディアクエリに依存すると、多種多様な端末に適切な大きさが対応しきれない。コンテンツを基準に考えるという考え方が必要。

コンテンツを基準に考えることにも技術的な問題がある。メディアクエリは、「ビューポート」の幅に対応するものであって、コンテンツにとっての実際の利用可能なスペースには関係しないのです。コンポーネントが配置されるのは ? pxの幅のコンテナの中であり、ビューポートの幅が同じであれば、再構成するための手がかりがない。 <- よくわからん

flex-basisを使ったコンテキストに応じた制御

.parent {
  display: flex;
  flex-wrap: wrap;
}

.parent > * {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 30ch; 
}

子要素は、初めは「30文字分くらいの幅」を持ち、余裕があれば広がり、狭いと縮むので、どんな画面幅でもバランスよく表示される。

mdnより

このデモでは最初のフレックスアイテムに設定する flex-basis 値を変更し、利用できる空間いっぱいに伸長させたり縮小させたりします。他のフレックスアイテムもサイズが変更され、少なくとも min-content のサイズになります。例えば、最初のアイテムの flex-basis を 200px に設定すると、始めは 200px ですが、利用できる空間に合うように縮小されます。

flex-basisの値には、基本的には対象となる子要素の「理想的」な幅を指定する。

デザインする上で要素のサイズはあくまで「理想的」なものと考え、ある程度の増減を許容できればメディアクエリのブレイクポイントは不要。

水平方向のレイアウト

.with-sidebar {
  display: flex;
  flex-wrap: wrap;
}

.sidebar {
  flex-basis: 20rem;
  flex-grow: 1;
}

.not-sidebar {
  flex-basis: 0;
  flex-grow: 999;
}

折り返し位置を制御する

.with-sidebar {
  display: flex;
  flex-wrap: wrap;
}

.sidebar {
  flex-basis: 20rem;
  flex-grow: 1;
}

.not-sidebar {
  flex-basis: 0;
  flex-grow: 999;
  min-width: 50%;
}

gutterの設定

.with-sidebar {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.sidebar {
  /* サイドバーがサイドバー足りうる値 */
  flex-basis: 20rem;
  flex-grow: 1;
}

.not-sidebar {
  /* 0から伸縮する */
  flex-basis: 0;
  flex-grow: 999;
  /* 要素の幅が等しくなった場合に折り返す */
  min-width: 50%;
}

サイドバーの幅を規定せずに内在的なコンテンツによって幅を決める場合は flex-basis に何も設定しなければ良い。

実装例

.with-sidebar {
  display: flex;
  flex-wrap; wrap;
  gap: var(--gutter, var(--s1))
}

.with-sidebar > :first-child {
  /* サイドバーがサイドバーたりうる値 */
  flex-basis: 20rem;
  flex-grow: 1;
}

.with-sidebar > :last-child {
  /* 0から伸長する */
  flex-basis: 0;
  flex-grow: 999;
  /* 要素の幅が等しくなった場合に折り返す */
  min-width: 50%;
}