【Next.js】両サイドにメニューがある場合のCSS設計
目標
- 左 :共通メニュー
- 中央:メインコンテンツ
- 右 :関連メニュー
左・右のどちらかは、お気に入りの表示や履歴といった常に表示することが目的となるようなコンテンツを置きたいと考えていました。
今回の設計では左にそれを配置し、右に目次等を置くことにしました。
どうHTML要素を配置するか
メニュー画面エリア内の設計
最初にメニュー画面(右・左)のエリア内を考えます。
position: fixed
はスクロール位置に関係なく配置することに適しています。ヘッダーの作成で利用される方も多いのではないでしょうか。
しかし、レスポンシブデザイン設計の際に位置をrem
単位やpx
単位等の微調整する作業が求められることと思います。
position: sticky を利用する
position: sticky
もスクロール中にtop
を指定すると固定が可能です。<div>
や<section>
の中から出る際にコンテンツと共にスクロールされ、HTML要素の親子関係が維持された設計が可能になるため今回採用しました。
画面全体の要素配置
次に、画面全体の配置を考えます。
display: flex
を複数回使うことで全体のレイアウトを構築しました。
まずは、下の画像をご覧ください。
- 外枠は
flex
プロパティとし、子要素に2個div
を配置します。 - 1個目を共通メニューとし、2個目を
flex flex-row-reverse
プロパティにし、子要素に2個div
を配置します。 - 1個目を関連メニューとし、2個目をメインコンテンツにして完成です。
flex-row-reverse
を使いました。通常のCSS表記は下の通りです。
.class {
flex-direction: row-reverse;
}
これは、子要素を右から横に並べて配置する宣言です。flex-row
を使わないことで、レスポンシブデザイン適応時にdisplay: block
と宣言するだけで画像のオレンジ色の要素が「関連メニュー」→「メインコンテンツ」の順で縦に並びます。
コンポーネントとして扱う
Next.js環境下なのでこれらをコンポーネント化します。
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="ja">
{/* 略 */}
<body className="min-h-vh">
<Header />
{/* (画像の黒色の要素) */}
<div className="md:flex justify-center">
<Menu />
{children}
</div>
<Footer />
</body>
</html>
);
}
// (画像の青色1個目の要素)
export default function Menu() {
return <div className="relative flex-grow-0 min-w-72 hidden xl:block">
<div className="w-full sticky top-14">
{/* 要素を記述 */}
</div>
</div>
}
// (画像の青色2個目の要素)
export function Main({ children }: { children?: React.ReactNode }) {
return <main className="flex-row-reverse flex-grow md:flex md:flex-grow-0">
{children}
</main>
}
// (画像のオレンジ色1個目の要素)
// md以上で要素を表示
export function Side({ children }: { children?: React.ReactNode }) {
return <div className="hidden w-44 md:block lg:min-w-64">
<div className="w-full sticky top-14">
{children}
</div>
</div>
}
// (画像のオレンジ色1個目の要素)
// md未満ではstickyとなり画面トップにくっつく
export function SideMDShown({ children }: { children?: React.ReactNode }) {
return <div className="sticky md:relative md:w-44 lg:min-w-64 top-0 z-20 md:z-auto">
<div className="w-full sticky top-14 flex flex-row-reverse md:block">
{children}
</div>
</div>
}
// (画像のオレンジ色2個目の要素)
export function Section({ children }: { children?: React.ReactNode }) {
return <section className="p-8 rounded-3xl w-full md:w-[34rem] lg:w-[44rem] mx-auto xl:m-0">
{children}
</section>
}
大きさ指定は残した状態で記述してみました。右メニューは、必須でない場合にレスポンシブで非表示にするパターンも作りました。
これを使ったpage.tsx
の記述方法も記載します。
// import は省略
export default function TagList() {
return <Main>
<Side>
右に書く内容
</Side>
<Section>
メインコンテンツはここに
</Section>
</Main>
}
スッキリと書けました。編集する際もページ毎のレイアウトのズレも心配することなく、記述できるのではないでしょうか。