10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【CSS】ありのままで〜幅、親に合わせて〜【Flexbox】【width: stretch】

Posted at

この記事のように、width: stretch が一部のブラウザでリリースされたことで、「width: 100% ではなくてこちらを使うべき」という受け止め方がされています。

しかし、要素の横幅について、もっと気を付けておくべきこと が見過ごされています。


それは、横幅は、余計なことをしなければ、親に合わせて勝手に良い感じになってくれる ということです。

OK の例では、カード内の各子要素がカードの幅いっぱいに広がっている。 DON'T の例では、カード内の各子要素がカードの左側に寄っていて、右下のボタンを右側に寄せることに失敗している。

CodeSandbox あります

良い例、悪い例をまとめてカンタンに確認できる CodeSandbox を用意しました。

わかりやすさのために表示している枠線を消したり、width: stretch を付け外ししたりして確認できます。

✅️ OK: デフォルトで良い感じになる

まず、良い例から出しましょう。HTML と CSS(ネスト機能を利用しています)の抜粋は以下の通りです。

  • .card : カード本体、縦向きの flexbox
    • .card__title : カードのタイトル
    • .card__description : 本文
    • .card__footer[data-align="end"]: ボタンたちを横向きに右揃えで配置する
<section class="card">
  <h2 class="card__title">カード #1</h2>
  <p class="card__description">春はあけぼの、やうやう白くなりゆく山際</p>
  <div class="card__footer" data-align="end">
    <button type="button" class="card__button" data-color="danger">
      削除
    </button>
    <button type="button" class="card__button">
      編集
    </button>
  </div>
</section>
.card {
  display: flex;
  flex-direction: column;
  padding: 8px 0;
  /* 中略: ボーダー設定など */
}

.card__title {
  margin: 0;
  padding: 0 16px;
}

.card__description {
  margin: 0;
  padding: 0 16px;
}

.card__footer {
  display: flex;
  padding: 0 16px;

  &[data-align="end"] {
    justify-content: end;
  }
}

.card__button {
  appearance: none;
  border: none;
  /* 詳細は省略 */
}

これだけだと、どこが「良い」「ありのまま」のか分かりにくいと思うので、さっそく「ダメ」「余計」な例を見てみましょう。

ダメな例: 余計な align-items: start

ここで、「親」である .cardalign-items: start を追加してみましょう。

  .card {
    display: flex;
    flex-direction: column;
+   align-items: start;
    padding: 8px 0;
    /* 中略: ボーダー設定など */
  }

なんか、子要素はだいたい左側に寄ってるから、とりあえず左に寄せとけばええやろ

と軽い気持ちで書いてしまいがちなこの一行ですが……

OK の例では、カード内の各子要素がカードの幅いっぱいに広がっている。 DON'T の例では、カード内の各子要素がカードの左側に寄っていて、右下のボタンを右側に寄せることに失敗している。

なんと、もともと横幅いっぱいに広がっていた各子要素が、己の自然な横幅まで縮んで、左側に寄ってしまいました……

タイトルや本文は無事ですが、フッター(.card__footer) は悲惨な状態です。

フッターは、それ自体が「子」であるボタンたちをレイアウトする立場にあるので、このように横幅が縮んでしまうのは痛手です。

width: stretch で帳尻合わせ

では、どうすれば良いのでしょうか?

フッター(含め各子要素は)の横幅は、自然に狭まってしまわず、親の幅いっぱいに広がるべき

という素朴な考えをそのまま CSS に落とし込んで、width: stretch (あるいは、 align-self: stretch)を使いたくなるでしょう。

  .card {
    display: flex;
    flex-direction: column;
    align-items: start;
    padding: 8px 0;
    /* 中略: ボーダー設定など */
  }

  .card__title {
+   width: stretch;
    margin: 0;
    padding: 0 16px;
  }

  /* 以下、
     .card__description, .card__footer 
     も同様… */

実際に、問題はこれで解決します。そう、表面的には……

align-items: start を設定してしまったほうも、スタイルが意図通りに戻った。

問題点: 子が親のことを知りすぎている

しかし、これを「OK のコード」と比較することによって、問題点が見えてきます。

  .card {
    display: flex;
    flex-direction: column;
+   align-items: start;    
    padding: 8px 0;
    /* 中略: ボーダー設定など */
  }

  .card__title {
+   width: stretch;
    margin: 0;
    padding: 0 16px;
  }

  .card__description {
+   width: stretch;
    margin: 0;
    padding: 0 16px;
  }

  .card__footer {
+   width: stretch;
    display: flex;
    padding: 0 16px;

    &[data-align="end"] {
      justify-content: end;
    }
  }

  .card__button {
    appearance: none;
    border: none;
    /* 詳細は省略 */
  }

そうです。親(.card)が余計な設定を追加したことによって、子側で余計な記述が必要になっています。

純粋にコードの量が増えています。「こちらが減って、あちらが増えた」というトレードオフさえありません。

このように「根本的な問題を解決せず、末端のワークアラウンドで何とかする」「子が親のことを知る必要がある」のがマズい状況だということは、プログラミング一般の知識として共有されていますが、CSS でのスタイリングについても同様です。

特に、React で開発していて、以下のように組み合わせて使用するコンポーネント群(Compound Component パターン)を実装したい場合には、コンポーネント実装コードの理解しづらさにつながり、メンテナンスが困難になります。

<Card.Root>
  <Card.Title>カード #1</Card.Title>
  <Card.Description>
    春はあけぼの、やうやう白くなりゆく山際
  </Card.Description>
  <Card.Footer align="end">
    <Card.Button color="danger">削除</Card.Button>
    <Card.Button>編集</Card.Button>
  </Card.Footer>
<Card.Root>  

ちなみに、OK の例では、.cardalign-items は初期値(auto)のままにしましたが、明示的に align-items: stretch と明示しても効果は同じです。

https://developer.mozilla.org/ja/docs/Web/CSS/align-items

ちなみに、align-items が初期値(= stretch)の Flexbox の、子要素たちの横幅を制御する挙動は、Flexbox ではなく単なる display: block のときと同様です。

実は、今回のコード例では Flexbox の機能を全く活用していないので、 display: block にしても全く問題ありません。たとえば「カードを3カラムで並べて表示するので、 .card__descriptionflex-grow: 1 による高さ調整が必要で、 Flexbox を使った」といったテイでご容赦ください。

まとめ: じゃあいつ width: stretch が必要なのか?。

この記事では、width: stretch に飛びつく前に知っておくべきこととして、「親の Flexbox を注意深く書けば(初期値だと案外うまくくいきがち)、そもそも width: stretch を使うまでもない」ということを提示しました。

ちなみに、CSS Grid や、通常の display: block についても同様です。

特に、CSS Grid は、親子の役割分担の明確化という点では、Flexbox よりもかなり有効なので、CSS Grid を第一の選択肢として、Flexbox はその次と捉えるのが良いと思います。(この記事で Flexbox を使ったのは、単に説明のしやすさのためです。)

width: stretch が必要になるケースは、下記の記事にうまくまとまっています。 <img><button> のようなインラインの要素は、デフォルトで己の自然な横幅まで縮んでしまうので必要……

と思いきや、

  • 親が Flexbox か CSS Grid の場合
    • → 今回の記事のやりかたの延長でインラインの要素の表示問題も解決される *1
    • ただ、Flexbox で特殊なレイアウト制御をするために、width: stretch を使うことはありそう
  • 親がそれ意外の場合
    • たしかに、その場合には width: stretch にしたほうが良いかも…
    • → いや、でも、親を Flexbox か CSS Grid にすれば済む話では?

ということで、

「親を Flexbox や CSS Grid に書き換えることが現実的でない」という事情がある場合など、限られた状況でのみ width: stretch が必要になる。

と考えて差し支えないでしょう。


補足: 親が Flexbox か CSS Grid の場合、image や select を横幅いっぱいにするには、このような対応で十分です。

<img
  src="https://placebear.com/160/16"
  width="160"
  height="16"
  class="card__image"
  data-responsive
/>
/* select と button は特別な対応不要 */
.card__image[data-responsive] {
  display: block;
  height: auto;
  width: auto;
}

カードの子要素として、横幅か小さすぎる画像や大き過ぎる画像を貼っても横幅ぴったりに表示されている様子

あとがき

CSS はバタフライ・エフェクト的に影響が広がりやすく、今回のように「こちらが立てばあちらが立たず」で場当たり的に書いてしまうことが多々あります。

そのように書き散らしたコードそのままでは、あとになってメンテナンスが困難になる可能性があります。

「この記述は、本当に必要なのか?」「《親子間の相互作用》を、もっと分かりやすくできるのでは?」と自問したり、レビューを受けたりすることが重要であり、

世の中で軽視されがちな「トリビア的な細かい知識」は、このようなときに役立ちます。

あとがきのあとがき: 「知識」軽視について

近ごろ、「知識」を軽視する意見をぱらぱらと見かけます

『知識』は情報を知っていることを指すのに対し、『知恵』は知っているだけでなくそれを活かす能力だと言われています。

―― 【完全解説】なぜ人はアウトプットができないのか?より孫引き。引用元不明

のように、「知識」と「知恵」という日常語に対して、意味を勝手に後付けしてうんちくのようにしてしまうのは、不要な言葉遊びです。

こういった議論は、「陳述記憶」「非陳述記憶」といった神経心理学の言葉や、ナレッジマネジメントの言葉でなされるべきだと思っています。

そんなことをしなくても、「知識」と「知識を実際に活用する能力」と言えばそれで済む話ですし。

たしかにプログラミングは「自転車の乗り方」のような《非陳述記憶》がモノを言う側面も大いにあります。

実際に自分の手を動かした経験が「知識」の「知恵」化につながる(つまり、《意味記憶した内容》を、実際の作業で活用できるようになる)のも事実です。

しかし、「手を動かす」ことを過大評価して、「手を動かした」ことで満足するようでは不十分だと思います。

今回述べた通り、「このコードは無駄な遠回りではないか」「ツールやライブラリに正しく頼るべきではないか」と立ち止まって検討する必要がありますが、

その検討のためには、「知識」すなわち《意味記憶の引き出し》を豊富にしておくことも重要だと考えています。

10
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?