0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React + CSS Modules 実践レイアウト奮闘記:Flexbox, Sticky, そしてコンポーネント設計

Posted at

React + CSS Modules 実践レイアウト奮闘記:Flexbox, Sticky, そしてコンポーネント設計

はじめに

Reactでコンポーネントベースの開発を進めていると、複数のコンポーネントを組み合わせて一つのページを構築する際に、予期せぬレイアウトの崩れに直面することがあります。今回は、ECサイトの商品詳細ページを実装する過程で遭遇した、いくつかの具体的なレイアウト問題とその解決プロセスを共有します。Flexboxとposition: stickyを中心に、コンポーネントの構造とCSSをどのように改善していったのかを段階的に見ていきましょう。

Case 1:カラム内の要素幅がバラバラで見栄えが悪い

最初に直面したのは、ページの左カラムに配置した商品情報、カテゴリー表示、そして修正・削除ボタンの幅がそれぞれ異なり、見た目が揃っていない問題でした。

原因

  • 親コンテナである.leftが、子要素の幅を制御していなかった。
  • 各子コンポーネント(ProductVerticalInfo, ProductCategory, Button)が、それぞれ固有の幅を持っていた。

解決策:親が基準を定め、子はそれに従う!

この問題に対する最も確実な解決策は、親コンテナに明確な幅を設定し、すべての子要素がその幅を100%継承するように(親の幅いっぱいに広がるように)設定することです。

1. 親コンテナに固定幅を設定 (product-details.module.css)

まず、左カラム全体をラップしている.leftクラスに固定幅を割り当てます。

/* product-details.module.css */
.left {
  /* 1. カラムの基準となる幅を設定 */
  width: 280px; 
  flex-shrink: 0; /* flexコンテナが縮小する際に、このアイテムが縮まないようにする */

  /* 既存のスタイル */
  display: flex;
  flex-direction: column;
  gap: 16px;
}
Use code with caution.
Markdown
2. 子コンポーネントの幅を100%に設定
次に.leftの直下にある各コンポーネントのルート要素最上位のコンテナにwidth: 100%のスタイルを適用しますこれによりすべての子要素は親が定めた280pxという幅いっぱいに広がります
/* ProductCategory.module.css */
.categoryContainer {
  width: 100%; /* 既存の固定値 (e.g. 12.3rem) から変更 */
  /* ... */
}

/* ProductVerticalInfoコンポーネントのCSS */
.container { /* ルート要素のクラス名だと仮定 */
  width: 100%;
  /* ... */
}

/* product-details.module.css */
.sellerButtons {
  width: 100%;
  /* ... */
}
/* 補足: Buttonコンポーネント自体もwidth: 100%にする必要があります */
Use code with caution.
Css
このアプローチにより左カラムのすべての要素が美しく整列しました
Case 2:スクロールに追従するフローティングボタンの実装
ページが長くなるとユーザーがレビューセクションに移動したりページ最上部に戻ったりする操作が煩雑になりますそこでスクロールに追従する便利なトグルボタンを実装することにしました
試行1position: fixed --- シンプルだがレイアウトから浮いてしまう
最も簡単な方法はposition: fixedを使うことですこれによりボタンは常に画面の特定の位置に固定されます
/* product-details.module.css */
.toggle {
  position: fixed;
  right: 40px;
  bottom: 40px;
  z-index: 1000;
}
Use code with caution.
Css
しかしこの方法ではボタンがページ全体のレイアウトから完全に分離されてしまい他のカラムとの関係性を保てないという問題がありました
試行2position: sticky --- スクロール位置に応じて追従させたい
次に試したのがposition: stickyですこれは要素が特定のスクロール位置に達するまでは通常の位置にあり達した瞬間に固定配置に切り替わる便利なプロパティです
/* product-details.module.css */
.right {
  /* 右カラム全体を追従させる */
  position: sticky;
  top: 20px; /* 画面上部から20pxの位置で固定 */
  align-self: flex-start; /* sticky要素が親要素の高さまで伸びるのを防ぐ */
}
Use code with caution.
Css
しかしここで重要な注意点がありますposition: stickyはスクロールする親コンテナの中でしか機能しません 当初のHTML構造では追従させたい要素の親がスクロール範囲に含まれていなかったためうまく動作しませんでした
最終的に追従させたい購入オプションフォーム(ProductSelectionForm)とトグルボタンを右カラムを担う一つのコンテナ(.right)にまとめそのコンテナ自体にposition: stickyを適用することで意図した通りの挙動を実現できました
// ProductDetailsPage.tsx
<div className={styles.container}>
  <div className={styles.left}> {/* 左カラム */} </div>
  <div className={styles.middle}> {/* 中央カラム */} </div>
  
  {/* 右カラムを一つのコンテナにまとめる */}
  <div className={styles.right}>
    {/* トグルボタンや購入フォームなどをここに入れる */}
  </div>
</div>
Use code with caution.
Jsx
Case 3:「レビューへ/トップへ戻るボタンのUX改善
当初は一つのトグルボタンでレビューへ移動トップへ戻るの2つの機能を兼用させていましたがこれではユーザーが混乱する可能性がありました
解決策役割を明確に分離する
最終的に、2つのボタンを配置するシンプルな方法に落ち着きました
レビューへ移動ボタン: ページの右カラム上部に常に表示されクリックするとレビューセクションへスクロールする
トップへ戻るボタン: レビューセクションの横に配置しクリックするとページ最上部へスクロールする
// ProductDetailsPage.tsx

// ...
<div className={styles.middle}>
  {/* ... 商品説明など ... */}
  
  {/* レビューセクションと「トップへ戻る」ボタンをまとめる */}
  <div className={styles.reviewSectionContainer} ref={reviewListRef}>
    <ReviewList {...} />
    <div className={styles.toTopToggle}>
      <ReviewToggle reviewIsOn={true} setReviewIsOn={() => handleToggleClick(false)} />
    </div>
  </div>
</div>

<div className={styles.right}>
  {/* 「レビューへ」ボタンを配置 */}
  <div className={styles.reviewGoToggle}>
      <ReviewToggle reviewIsOn={false} setReviewIsOn={() => handleToggleClick(true)} />
  </div>
  <ProductSelectionForm {...} />
</div>
Use code with caution.
Jsx
CSSではreviewSectionContainerにposition: relativeを指定しその子要素であるtoTopToggleにposition: absoluteを使うことでレビューリストの横に綺麗に配置しました
.reviewSectionContainer {
  position: relative;
  margin-top: 40px;
}
.toTopToggle {
  position: absolute;
  top: 0;
  right: -80px; /* リストの右外側に配置 */
}
Use code with caution.
Css
これにより各ボタンの役割が明確になり直感的なユーザー体験を提供できるようになりました
まとめ
今回の実装を通して学んだ重要なポイントは以下の通りです
Flexboxでは親がレイアウトの基準: 子要素の幅を揃えたいときはまず親コンテナに明確な幅固定または比率を設定することが重要です
position: stickyの動作条件を理解する: stickyはスクロール可能な親コンテナ内でのみ機能します意図通りに動かないときはHTMLの構造を見直してみましょう
複雑なUIは役割を分割する: 一つの要素に多機能を持たせるより役割を分割して複数の要素で実現するほうが実装がシンプルになりユーザーにとっても分かりやすくなることがあります
レイアウトの崩れは些細なことのように思えますがユーザー体験に大きく影響しますもし同じような問題に直面したらこの記事が少しでもヒントになれば幸いです
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?