はじめに
先日、実務でコーディングしたグローバルナビのHTML、CSSコーディングについてじっくり解説します。
参考になれば幸いです。
デザイン
このような、よくあるサイト名とナビゲーションのメニュー項目が横並びになったレイアウトです。
デザイン仕様の確認
コーディングを始める前に、デザインカンプからコーディングに必要な情報を抜き出して整理します。
メニュー項目
メニューの項目数は4つ。ただし、運用しているうちにメニューが増えるというのはよくある話なので、項目数に依存しにくいコードを意識します。
マウスホバーすると、文字色が変化するとともに、文字の幅と同じ分だけ下線が伸びるというデザインです。
リンク領域は、文字の高さ分だけにしてしまうと非常にクリックしにくいため、帯部分の高さいっぱいまでとるようにします。
こういったデザインデータで表現されない部分を考えるのはコーダーの領域かなと思います。(チームによって異なると思いますが)
ナビゲーションの白背景の部分は横幅100%で、中身は1240pxまで伸びます。ただし、ウィンドウ幅が狭くなった際にも、左右に30pxの余白は持つようにします。
また、ブラウザ幅が狭い場合(スマホ)のデザインも実装しますが、ボタンの挙動やメニューの中身などは今回は割愛します。
HTML設計
デザイン仕様が確認できたので、次にHTML構造を考えていきます。
まずいちばん外側のHTMLから、以下のような構造を作ります。
header (背景色が白の部分)
┗ div (横幅1240pxの範囲)
┗ h1(サイト名)
┗ nav(ナビゲーション)
また、nav
要素の中は以下のようにします。
nav
┗ ul(スマホでは非表示)
┃ ┗ li
┃ ┗ a
┃ ┗ li
┃ ┗ a
┃ ┗ li
┃ ┗ a
┃ ┗ li
┃ ┗ a
┗ button(PCでは非表示)
┗ span(横線の装飾用)
┗ span(横線の装飾用)
┗ span(横線の装飾用)
レイアウト的にはnav
要素とul
要素の両方ある必要はないですが、スマホサイトで表示されるボタン部分も含めてnav
の範囲とするためこのようなマークアップにしました。
コーディング
それでは実際にHTMLとCSSをコーディングします。
この記事ではできるだけレイアウトに限った話だけにしたいので、Reset.cssを読み込んでブラウザのデフォルトスタイルを無効にしています。
また、CSSのコーディングにはプリプロセッサーにSCSSを使用しています。
サイト名とナビゲーションを横並びに配置する
まずはサイト名とナビゲーションを横並びに配置するレイアウトを組みます。
<header class="header">
<div class="header__inner">
<h1 class="header__siteTitle">
<div style="outline: 1px solid red;">確認用のダミー要素</div>
</h1>
<nav class="header__nav">
<div style="outline: 1px solid blue;">確認用のダミー要素</div>
</nav>
</div>
</header>
.header {
width: 100%;
height: 90px;
background-color: #fff;
box-shadow: 0 1px 1px rgb(0 0 0 / 10%);
}
.header__inner {
display: flex;
align-items: stretch;
justify-content: space-between;
width: 1240px;
max-width: calc(100vw - 30px);
height: 100%;
margin: 0 auto;
}
.header
で背景色が白い部分を作り、.header__inner
で横幅1240pxを定義すると同時に、display: flex;
とjustify-content: space-between;
でh1
とnav
を左右に振り分けています。
align-items: stretch;
は、フレックスアイテムの要素が高さいっぱいに広げるため指定しています。中の要素にheight: 100%;
でも同じ結果になるでしょう。
ここまでの見た目
サイト名部分をコーディング
シンプルな左半分を先に組んでおきます。
<header class="header">
<div class="header__inner">
<h1 class="header__siteTitle">
<a href="#">Awesome Site Title</a> </h1>
<nav class="header__nav">
<div style="outline: 3px solid blue;height:100%;">確認用のダミー要素</div>
</nav>
</div>
</header>
$breakPoint: 900px;
.header {
width: 100%;
height: 90px;
background-color: #fff;
box-shadow: 0 1px 1px rgb(0 0 0 / 10%);
}
.header__inner {
display: flex;
align-items: stretch;
justify-content: space-between;
width: 1240px;
max-width: calc(100vw - 30px);
height: 100%;
margin: 0 auto;
}
.header__siteTitle { font-size: 32px; @media only screen and (max-width: $breakPoint) { font-size: 24px; } a { display: flex; align-items: center; height: 100%; color: inherit; text-decoration: none; transition: color .2s; &:hover { color: skyblue; } }}
display: flex;
やalign-items: center;
を使って縦位置中央揃えにしたり、ホバー時の変化を実装したりしています。
また、スマホ時の見た目の実装のため、$breakPoint: 900px;
という変数を追加し、メディアクエリを実装しています。
実際の現場ではこのあたりはもう少し使い回しのしやすいmixinにしているのですが、今回は簡易的に実装しました。
ここまでの見た目
ナビゲーションを追加
右半分のナビゲーション部分を組んでいきます。
<header class="header">
<div class="header__inner">
<h1 class="header__siteTitle">
<a href="#">Awesome Site Title</a>
</h1>
<nav class="header__nav">
<ul class="header__navList"> <li class="header__navListItem"> <a class="header__navLink" href="#">Sightseeing</a> </li> <li class="header__navListItem"> <a class="header__navLink" href="#">Festivals</a> </li> <li class="header__navListItem"> <a class="header__navLink" href="#">History</a> </li> <li class="header__navListItem"> <a class="header__navLink" href="#">Food & Souvenirs</a> </li> </ul> </nav>
</div>
</header>
$breakPoint: 900px;
.header {
width: 100%;
height: 90px;
background-color: #fff;
box-shadow: 0 1px 1px rgb(0 0 0 / 10%);
}
.header__inner {
display: flex;
align-items: stretch;
justify-content: space-between;
width: 1240px;
max-width: calc(100vw - 30px);
height: 100%;
margin: 0 auto;
}
.header__siteTitle {
font-size: 32px;
@media only screen and (max-width: $breakPoint) {
font-size: 24px;
}
a {
display: flex;
align-items: center;
height: 100%;
color: inherit;
text-decoration: none;
transition: color .2s;
&:hover {
color: skyblue;
}
}
}
.header__nav { // あとで使うのでセレクターだけ書いておく}.header__navList { display: flex; height: 100%;}.header__navListItem { &:not(:last-child) { margin-right: 40px; }}
.header__navList
にdisplay: flex;
し各メニュー項目を横並びにします。
また、.header__navListItem
のlast-child以外に右余白を設定し項目間の余白を作ります。
ここまでの見た目
ナビゲーションの項目の見た目を整える
ナビゲーションの各メニュー項目について、ホバー時の装飾以外の部分を整えていきます。
HTMLに変更はありません。
$breakPoint: 900px;
.header {
width: 100%;
height: 90px;
background-color: #fff;
box-shadow: 0 1px 1px rgb(0 0 0 / 10%);
}
.header__inner {
display: flex;
align-items: stretch;
justify-content: space-between;
width: 1240px;
max-width: calc(100vw - 30px);
height: 100%;
margin: 0 auto;
}
.header__siteTitle {
font-size: 32px;
@media only screen and (max-width: $breakPoint) {
font-size: 24px;
}
a {
display: flex;
align-items: center;
height: 100%;
color: inherit;
text-decoration: none;
transition: color .2s;
&:hover {
color: skyblue;
}
}
}
.header__nav {
}
.header__navList {
display: flex;
height: 100%;
}
.header__navListItem {
&:not(:last-child) {
margin-right: 40px;
}
}
.header__navLink { display: flex; align-items: center; height: 100%; color: inherit; text-decoration: none;}
a
要素である.header__navLink
にdisplay: flex;
やalign-items: center;
、height: 100%;
を指定して、文字を縦位置中央に配置します。
ここまでの見た目
ホバー時の装飾を追加
HTMLに変更はありません。
$breakPoint: 900px;
.header {
width: 100%;
height: 90px;
background-color: #fff;
box-shadow: 0 1px 1px rgb(0 0 0 / 10%);
}
.header__inner {
display: flex;
align-items: stretch;
justify-content: space-between;
width: 1240px;
max-width: calc(100vw - 30px);
height: 100%;
margin: 0 auto;
}
.header__siteTitle {
font-size: 32px;
@media only screen and (max-width: $breakPoint) {
font-size: 24px;
}
a {
display: flex;
align-items: center;
height: 100%;
color: inherit;
text-decoration: none;
transition: color .2s;
&:hover {
color: skyblue;
}
}
}
.header__nav {
// あとで使うのでセレクターだけ書いておく
}
.header__navList {
display: flex;
height: 100%;
}
.header__navListItem {
&:not(:last-child) {
margin-right: 40px;
}
}
.header__navLink {
position: relative; display: flex;
align-items: center;
height: 100%;
color: inherit;
text-decoration: none;
transition: color .2s; &::after { position: absolute; right: 0; bottom: 0; left: 0; display: block; width: 0; height: 5px; margin: auto; content: ""; background-color: skyblue; transition: width .2s; } &:hover { color: skyblue; &::after { width: 100%; } }}
.header__navLink
にafter擬似要素を追加し、ホバー時の装飾を実装します。
after擬似要素は、線の色や幅などをしておき、いったんwidth
を0にします。そして、ホバー時にwidth: 100%;
として表示しています。
ホバー時に文字色も変化させます。下線の表示と文字色の変化を、どちらも0.2秒でアニメーションするように指定しています。
ここまでの見た目
スマホ時の見た目の実装
最後に、スマホサイトでの表示を実装します。
複雑なのはボタン部分の実装ですが、こちらは過去に別記事で解説を書いたため、詳細な解説は割愛します。
スマホサイトでよくみるハンバーガーメニューの開閉ボタンのCSSをじっくりコーディング
<header class="header">
<div class="header__inner">
<h1 class="header__siteTitle">
<a href="#">Awesome Site Title</a>
</h1>
<nav class="header__nav">
<ul class="header__navList">
<li class="header__navListItem">
<a class="header__navLink" href="#">Sightseeing</a>
</li>
<li class="header__navListItem">
<a class="header__navLink" href="#">Festivals</a>
</li>
<li class="header__navListItem">
<a class="header__navLink" href="#">History</a>
</li>
<li class="header__navListItem">
<a class="header__navLink" href="#">Food & Souvenirs</a>
</li>
</ul>
<button class="header__navButton"> <span class="header__navButtonLineTop"></span> <span class="header__navButtonLineMiddle"></span> <span class="header__navButtonLineBottom"></span> </button> </nav>
</div>
</header>
$breakPoint: 900px;
.header {
width: 100%;
height: 90px;
background-color: #fff;
box-shadow: 0 1px 1px rgb(0 0 0 / 10%);
}
.header__inner {
display: flex;
align-items: stretch;
justify-content: space-between;
width: 1240px;
max-width: calc(100vw - 30px);
height: 100%;
margin: 0 auto;
}
.header__siteTitle {
font-size: 32px;
@media only screen and (max-width: $breakPoint) {
font-size: 24px;
}
a {
display: flex;
align-items: center;
height: 100%;
color: inherit;
text-decoration: none;
transition: color .2s;
&:hover {
color: skyblue;
}
}
}
.header__nav {
display: flex; align-items: center;}
.header__navList {
display: flex;
height: 100%;
@media only screen and (max-width: $breakPoint) { display: none; }}
.header__navListItem {
&:not(:last-child) {
margin-right: 40px;
}
}
.header__navLink {
position: relative;
display: flex;
align-items: center;
height: 100%;
color: inherit;
text-decoration: none;
transition: color .2s;
&::after {
position: absolute;
right: 0;
bottom: 0;
left: 0;
display: block;
width: 0;
height: 5px;
margin: auto;
content: "";
background-color: skyblue;
transition: width .2s;
}
&:hover {
color: skyblue;
&::after {
width: 100%;
}
}
}
.header__navButton { position: relative; display: none; width: 50px; height: 50px; background-color: #000; border: none; border-radius: 50%; @media only screen and (max-width: $breakPoint) { display: block; }}@mixin _navButtonLine { position: absolute; right: 0; left: 0; display: block; width: 26px; height: 2px; margin: auto; background-color: #fff;}.header__navButtonLineTop { @include _navButtonLine; transform: translateY(-10px);}.header__navButtonLineMiddle { @include _navButtonLine;}.header__navButtonLineBottom { @include _navButtonLine; transform: translateY(10px);}
.header__nav
に指定したdisplay: flex;
とalign-items: center;
は、スマホのボタンを縦位置中央に配置するためのコードで、PCには必要ありません。
悪い影響もないためメディアクエリーを指定していませんが、厳密にやるならメディアクエリーの中だけに指定してPCに影響しないよう組むのが良いでしょう。
.header__nav {
@media only screen and (max-width: $breakPoint) {
display: flex;
align-items: center;
}
}
これで、実装はすべて完了です。
終わりに
以上です。
サイト名とメニューが横並びのシンプルな構成ですが、綺麗に組むことに困っている方の参考になればと思い記事にしてみました。
完成形のソースコードはCodePenに載せましたので、よろしければご参考にどうぞ。
疑問点などあれば、ぜひTwitterなどでコメントいただければと思います^^
最後までお読みいただきありがとうございました。