CSS講座 / CSSの整理:共通スタイル・ネスト・カスケードレイヤー
保守しやすいCSSは、共通値、文書の基本、配置、部品、条件付き調整の役割を整理します。ネイティブネストは関連するセレクターをまとめ、カスケードレイヤーはルール群の優先関係を明示します。
重複とファイルの責任を整理する
同じ色・余白、使わないセレクター、似たボタンの指定を探します。同じ役割の値は変数へ、共通の部品は基本と必要な差分へ分けます。ただし重複をすべて消すために複雑な条件を作るより、独立した二部品の方が扱いやすい場合もあります。
/* Before cleanup: Same value repeated in multiple places */
.lesson-button { padding: .65rem 1rem; color: #166b92; }
.notice-button { padding: .65rem 1rem; color: #166b92; }
/* After organizing: sharing common shapes and roles */
.button { padding: .65rem 1rem; color: var(--accent); }
.button.text-alert { color: #9d2632; }
これは整理方針の部分例です。共通クラスは実際のHTMLにも付けます。CSSの名前だけ変えても対応しません。tokens.cssは共有値、base.cssは文書とフォーカス、layout.cssは配置、components.cssは部品と状態、utilities.cssは明示的な小さな補助を担当させます。
小さい文書なら一ファイルをコメントで区切るだけでも十分です。分けただけで衝突がなくなるわけではなく、パスとカスケードも管理します。変数の範囲と継承を参照してください。
ネストの親自身と子孫を区別する
ネイティブCSSネストはブラウザーが直接解釈します。Sassなどがビルド時に別のCSSを生成する方式とは区別します。
.card {
& h2 { color: var(--accent); }
&:hover { border-color: var(--accent); }
}
& h2はカード内のh2、&:hoverはカード自身のホバーです。& :hoverと空白を入れるとホバーされた子孫になり、&.warningと& .warningも同じ要素と子孫の違いがあります。
/* The simple overlap above is expanded */
.card h2 { color: var(--accent); }
.card:hover { border-color: var(--accent); }
単一の親なら展開後のルールと比べやすくなります。ただしカンマで複数の親を列挙したネストでは、最も高い詳細度が影響する場合があり、単純な文字列連結ではありません。深すぎる入れ子を避け、未対応環境が必要なら展開したCSSや適切なビルドを選びます。MDNのネスト、ネストの詳細度を参照してください。
詳細度の前にレイヤーを比較する
同じ作者の通常宣言なら、レイヤー外がレイヤー内より優先されます。レイヤー内は、先に登録した順序の後のものが優先です。そのため後のレイヤーのクラスが前のレイヤーのIDに勝つ場合があります。
@layer tokens, base, layout, components, utilities;
@layer base { #layer-target { color: #183044; } }
@layer utilities { .text-alert { color: #9d2632; } }
このボタンは両セレクターに一致し、通常宣言では後のutilitiesの赤になります。同じ名前のレイヤーを後で開き直しても、そのレイヤー自体の順番は移動しません。
importantではレイヤーの順が逆になり、前のレイヤーが後より、レイヤー内が外より優先されます。これは同じ作者のスタイルでレイヤーを比べる説明であり、ユーザースタイル、インライン、動作中の遷移なども含む全条件をレイヤーだけで判断するものではありません。MDNの@layer、カスケードを参照してください。
HTMLと五つのCSSファイルを作る
空のフォルダーに次の構成を作り、すべてUTF-8で保存します。index.htmlのstyles/...は下のサブフォルダーを指します。外部ライブラリーやサーバーは不要です。
stylesheet-project/
index.html
styles/
tokens.css
base.css
layout.css
components.css
utilities.css
index.htmlにはリンクタグと実際のクラスがあります。比較ボタンは処理を実行するものではなく、色とフォーカスを観察するためのものです。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CSS files and layers by responsibility</title>
<link rel="stylesheet" href="styles/tokens.css">
<link rel="stylesheet" href="styles/base.css">
<link rel="stylesheet" href="styles/layout.css">
<link rel="stylesheet" href="styles/components.css">
<link rel="stylesheet" href="styles/utilities.css">
</head>
<body>
<main class="demo">
<h1>Organizing a small stylesheet</h1>
<article class="card">
<h2>Scope nested rules to the card</h2>
<p>The card heading uses the accent color. Compare it with the heading outside the card.</p>
<button class="button" type="button">Regular button</button>
</article>
<section><h2>Heading outside the card</h2><p>Use the default font color.</p></section>
<button id="layer-target" class="button text-alert" type="button">Layer comparison button</button>
</main>
</body>
</html>
styles/tokens.cssでレイヤー順を最初に登録し、共有値を置きます。
@layer tokens, base, layout, components, utilities;
@layer tokens {
:root { --ink: #183044; --accent: #166b92; --space-card: 1.25rem; }
}
styles/base.cssには文書の基本とフォーカスを置きます。IDの指定はレイヤーとの比較用です。
@layer base {
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 1rem; color: var(--ink); font-family: sans-serif; line-height: 1.6; }
h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
button { font: inherit; cursor: pointer; }
:focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
#layer-target { color: #183044; }
}
styles/layout.cssで領域の間隔を管理します。
@layer layout {
.demo { display: grid; gap: 1rem; max-width: 44rem; margin-inline: auto; }
.demo > h1 { margin-block: 0 .5rem; }
.demo > button { justify-self: start; }
}
styles/components.cssではカードとボタンを管理します。カード内の見出し色と、ホバー・内部のフォーカス時の枠を指定します。
@layer components {
.card {
padding: var(--space-card); border: 2px solid #90a4b2; border-radius: .5rem;
& h2 { margin-top: 0; color: var(--accent); }
&:hover, &:focus-within { border-color: var(--accent); }
}
.button {
padding: .65rem 1rem; color: var(--accent); background: #eaf4fa;
border: 1px solid #166b92; border-radius: .3rem;
}
}
styles/utilities.cssは比較用の補助クラスです。通常宣言では前のレイヤーより優先されます。
@layer utilities {
.text-alert { color: #9d2632; }
}
ファイル・ネスト・レイヤーを分けて試す
- カード内の見出しと通常ボタンは青、外の見出しは基本色、比較ボタンは赤かを見ます。Tabでフォーカスも確認します。
- componentsの& h2だけをコメントにし、カード見出しの色を比べて戻します。
- HTMLで両ボタンにtext-alertを付けると内側も赤になります。内側へ追加したクラスだけを外します。
- components末尾の@layerの外へ.button { color: #28633b; }を追加します。両方が緑になることを見て削除します。
- baseのID色だけをimportantにし、比較ボタンを見ます。utilitiesもimportantにしても前のレイヤーが保たれるか確認し、両方のimportantを外します。
- componentsへのHTMLリンクだけを外し、形が消える差を見て再接続します。
読み込めない指定と上書きを切り分ける
まずNetworkやSourcesでファイルが読めたか、次にStylesでセレクターが一致するかを見ます。ファイルがなければ保存、名前、拡張子、相対パスを調べます。ルールが見えなければHTMLクラスと構文対応を確認します。接続は外部CSSの適用を参照してください。
打ち消しがあればレイヤー、重要度、詳細度、スコープ、順番を追います。変数なら宣言範囲も見ます。不要なimportantを重ねず、責任や衝突の原因を直し、基本・ホバー・フォーカスを再確認します。




