【29日目】『リーダブルコード』を意識してHTML/CSSをリファクタリングしてみた
技術ブログ29日目。
本日は、HTML/CSSの読みやすさを追求します。
〇課題 ドロップダウンナビゲーション
一見すると機能しますが、HTMLがdivタグばかりで構造がわかりにくく、CSSも特定階層に依存した長いセレクタが多用されています。後からメニュー項目を増やしたり、デザインを変更したりするのが困難な状態です。
〇修正前コード(動くけれど読みにくいコード)
<div class="top-nav">
<div class="nav-container">
<div class="menu-item"><a href="/">ホーム</a></div>
<div class="menu-item has-sub">
<a href="#">サービス</a>
<div class="sub-menu">
<div class="sub-item"><a href="/s1">Web制作</a></div>
<div class="sub-item"><a href="/s2">アプリ開発</a></div>
<div class="sub-item"><a href="/s3">保守運用</a></div>
</div>
</div>
<div class="menu-item"><a href="/news">お知らせ</a></div>
<div class="menu-item"><a href="/contact">お問い合わせ</a></div>
</div>
</div>
.top-nav {
background: #333;
height: 60px;
}
.nav-container {
display: flex;
margin: 0 auto;
max-width: 1000px;
}
.menu-item {
position: relative;
line-height: 60px;
padding: 0 20px;
}
.menu-item a {
color: white;
text-decoration: none;
}
/* 複雑なネストと特定の構造への依存 */
.menu-item.has-sub:hover .sub-menu {
display: block;
}
.sub-menu {
display: none;
position: absolute;
top: 60px; /* 固定値(マジックナンバー) */
left: 0;
background: #444;
width: 200px;
line-height: 40px;
}
.sub-item {
padding: 0 15px;
border-bottom: 1px solid #555;
}
.sub-item:hover {
background: #555;
}
〇リファクタリングの指針
名著『リーダブルコード』に基づき、以下の点を意識しました。
無関係な下位問題を抽出する
メインのナビゲーションという大きな問題から、ドロップダウンという「独立した部品」を切り離します。これにより、ドロップダウン単体での再利用性が高まります。
一度に一つのことを
レイアウトの配置、項目の装飾、ホバー時の動作といったタスクを分離し、単純なルールの組み合わせで実装します。
セマンティックなHTML
divの羅列を卒業し、ナビゲーションにはnav、リストにはul/liなど、機械にも意味が伝わるタグを選定します。
〇修正後コード
<nav class="global-nav" aria-label="メインナビゲーション">
<ul class="nav-list">
<li class="nav-item">
<a href="/" class="nav-link">ホーム</a>
</li>
<li class="nav-item has-dropdown">
<a href="#" class="nav-link" aria-haspopup="true" aria-expanded="false">サービス</a>
<ul class="dropdown-menu">
<li class="dropdown-item"><a href="/s1">Web制作</a></li>
<li class="dropdown-item"><a href="/s2">アプリ開発</a></li>
<li class="dropdown-item"><a href="/s3">保守運用</a></li>
</ul>
</li>
<li class="nav-item">
<a href="/news" class="nav-link">お知らせ</a>
</li>
<li class="nav-item">
<a href="/contact" class="nav-link">お問い合わせ</a>
</li>
</ul>
</nav>
:root {
/* 変数による意図の明確化(Chapter 2) */
--nav-height: 60px;
--nav-bg-color: #333;
--dropdown-bg-color: #444;
--text-color: #fff;
--accent-color: #555;
--nav-max-width: 1000px;
}
/* 1. レイアウトのタスク(一度に一つのことを) */
.global-nav {
background-color: var(--nav-bg-color);
height: var(--nav-height);
}
.nav-list {
display: flex;
margin: 0 auto;
padding: 0;
max-width: var(--nav-max-width);
list-style: none;
height: 100%;
}
/* 2. ナビゲーション項目の装飾 */
.nav-item {
position: relative;
height: 100%;
}
.nav-link {
display: flex;
align-items: center;
padding: 0 20px;
height: 100%;
color: var(--text-color);
text-decoration: none;
}
/* 3. 下位問題:ドロップダウンの振る舞い */
.dropdown-menu {
display: none;
position: absolute;
top: var(--nav-height); /* 変数を利用してマジックナンバーを排除 */
left: 0;
min-width: 200px;
margin: 0;
padding: 0;
list-style: none;
background-color: var(--dropdown-bg-color);
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
}
.nav-item:hover .dropdown-menu {
display: block;
}
.dropdown-item a {
display: block;
padding: 12px 16px;
color: var(--text-color);
text-decoration: none;
border-bottom: 1px solid var(--accent-color);
}
.dropdown-item a:hover {
background-color: var(--accent-color);
}
〇主な修正ポイント
セマンティックなマークアップへの転換
nav、ul、liタグを採用。検索エンジンやスクリーンリーダーに構造を正しく伝えます。さらにaria属性を加え、アクセシビリティにも配慮しました。
無関係な下位問題の抽出
dropdown-menuという独立したクラスを作ることで、メインのナビゲーションを汚さずにメニュー専用のスタイルを定義しました。
一度に一つのことを
配置(Flexbox)、装飾(Color)、動作(Hover)をルールごとに整理。どこを直せば何が変わるかを予測しやすくしました。
マジックナンバーの排除と変数化 top: 60pxといった数値を--nav-heightという変数に置き換えました。ヘッダーの高さを変えても、1箇所の修正ですべてが連動する保守性を確保しています。
〇参照先
▼公式ドキュメント
▼書籍
リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック
以上
Discussion