15 年近くの経験を持つフロントエンド開発者として、私は Web 開発の進化を直接目撃してきました。私にとって、変更されたファイルを FTP 経由でアップロードしていた時代 (はい、GitHub は 15 年前に開始されましたが、私がそれを知ったのは 2011 年でした) から、応答性の高いインターフェイス、UI ライブラリ、Web サイトから直接生成された現代の時代まで、長い道のりを歩んできました。フィグマ。
それでも、次のような入れ子スタイルを採用するプロジェクトに今でも遭遇します。
.some-class ul li div a { /* some style */ }
また、
#nav .nav-link svg { /* some style */ }
衝撃的に思えるかもしれませんが、このようなコーディングの実践は、数百万ドル規模の急速に成長するプロジェクトから小規模なスタートアップ企業に至るまで、あらゆるものに浸透しています。
このアプローチが問題を引き起こす可能性がある理由を詳しく見てみましょう。
深くネストされたスタイルは、特に大規模なプロジェクトの場合、スタイルの競合を引き起こすことがよくあります。 CSS はカスケード スタイル シートとして、要素の特異性に応じて要素にカスケードして適用されます。深く入れ子になったスタイルは、その特殊性により、意図せずに他のスタイルをオーバーライドする可能性があります。
次の例を考えてみましょう。
.some-class ul li div a { color: red; } ... .some-class a { color: blue; }
.some-class
内のすべてのリンクが青色になることが予想されるかもしれません。ただし、最初のルールの方が特異性が高いため、 ul > li > div
内にネストされたリンクは青ではなく赤になります。これにより、予期しない視覚的な不一致が発生し、デバッグに多くの時間が無駄になる可能性があります。
CSS の特異性 (またはスタイルの「重み」) の概念を理解することは、深いネストがなぜ問題になるかを理解するために重要です。単一の要素に対して複数のルールが競合する場合、特異性によってどの CSS ルールが適用されるかが決まります。セレクターの種類と数に基づいて計算されます。
特異性は、4 つのカテゴリの重み付けシステムに基づいて計算されます。
ここで、次のルールを考えてみましょう。
body #content .data img:hover { /* some style */ }
特異度は0 1 2 2です。それは 1 つの ID ( #content )、2 つのクラス ( .dataと:hover )、および 2 つの要素 ( bodyとimg ) です。
ここで、次のルールを考えてみましょう。
#nav .nav-link svg { /* some style */ }
ここでの特異度は0 1 1 1です。それは 1 つの ID ( #nav
)、1 つのクラス ( .nav-link
)、および 1 つの要素 ( svg
) です。
特異性は、従来の 10 進数のような「繰り越し」システムでは機能しません。たとえば、10 進法では11と1+1は等価ですが、特異性0 1 0 11のセレクターは、特異性 0 1 1 1 と等しくありません。
最後に、ユニバーサル セレクター ( *
)、コンビネーター ( +
、 >
、 ~
、 ' ')、および否定擬似クラス ( :not()
) は、特異性には影響しません。ただし、 :not()
引数内では、セレクターは通常どおりカウントされます。
視覚的な学習者には、CSS の特異性に関するこのビデオをお勧めします。
CSS の特異性とその計算方法を理解すると、より適切で予測可能な CSS を記述し、スタイルが期待どおりに適用されない場合の問題をデバッグできるようになります。
!important
ルールと具体性開発者は、CSS の特異性の競合による問題に直面した場合、 !important
ルールの使用に頼ることがあります。このルールにより、CSS プロパティが非常に限定的になります。つまり、他のほとんどすべての宣言がオーバーライドされます。
例えば:
#nav .nav-link svg { color: blue; } .nav-link svg { color: red !important; }
最初のルールは ID セレクターにより高い特異性を持っていますが、2 番目のルールの!important
によりsvg
の色は赤になります。
!important
特異性の問題に直面した場合の迅速な解決策にはなりますが、広範囲に使用することはお勧めできません。過度に使用すると、保守性、予測可能性、パフォーマンスに影響を与える可能性があります。大規模なプロジェクトでは、 !important
の過剰使用は、CSS の特異性の管理に苦労していることを示していることがよくあります。多くの場合、 !important
に頼るよりも、CSS をリファクタリングし、過度に特殊なセレクターの使用を減らすことに時間を投資する方が良いでしょう。
今すぐ製品をチェックできます 🙂。私は自分のものを確認しました:
!important
簡単な修正としては魅力的かもしれません。それはナッツを割るために大ハンマーを使うようなものです。より保守しやすいアプローチは、セレクターをできるだけシンプルかつフラットに保つことです。これにより、将来の CSS の理解、管理、拡張が容易になります。そして覚えておいてください、 !important
戦争に勝つための最善の方法は、最初から戦争を始めないことです。
深くネストされたスタイルに関するもう 1 つの問題は、ブラウザーのレンダリングに対するパフォーマンスへの影響です。ブラウザーが要素にスタイルを適用すると、キー セレクター (この例ではa
とsvg
) から始まり、一致するものが見つかるか先頭に到達するまで、祖先を通って DOM を右から左に移動します。スタイルのネストが増えるほど、この走査にかかる時間が長くなり、パフォーマンスに影響を与え、大規模プロジェクトのページ読み込み時間が遅くなる可能性があります。
次のように CSS ルールを指定する場合:
.some-class ul li a { /* some style */ }
このルールは、ツリーの一番下 ( a
タグから) から始まり、ツリー全体に ( li
、 ul
、および.some-class
を介して) 進んでいく様子を視覚化できます。
ブラウザは最初にすべて(つまりすべて) のa
要素を検索し、次にこれらのa
タグがli
要素内にあるかどうかを確認します。その後、これらのli
要素がul
内にあるかどうかをチェックします。そして最後に、これらのul
.some-class
クラスの要素内にあるかどうかをチェックします。
これが、ブラウザーが CSS セレクターを読み取る方法と、複雑なセレクターがページのレンダリングの低下につながる理由です。ブラウザは、要素ごとに複数のチェックを行って、指定されたルールに適合するかどうかを確認する必要があります。ルールが深くなるほど、ブラウザが実行する必要があるチェックが増え、パフォーマンスに影響を与える可能性があります。
CSS モジュールを使用すると、作業しているコンポーネントをローカルにスコープする個々のモジュールに CSS を記述することができます。つまり、CSS モジュールのスタイルはその特定のモジュールにのみ適用され、ページ上の他の要素に漏れたり影響を与えたりすることはありません。
CSS モジュールがハッシュ化されたクラス名を使用してスタイルのカプセル化を保証する方法を見てみましょう。 CSS モジュールを使用している場合、CSS ファイルで定義したクラス名はコンパイル時にハッシュされます。このハッシュにより、コンポーネントに対応する一意のクラス名が作成されます。例を見てみましょう:
CSS モジュールが次のように定義されているとします。
/* Button.module.css */ .button { color: white; background-color: blue; }
そして、これをコンポーネント内で次のように使用します (私は、 styles
代わりにスタイル オブジェクトをs
としてインポートすることを好みます。これは、入力時間を節約し、コーディング効率を高めるための簡単なヒントです)。
import React from 'react'; import s from './Button.module.css'; const Button = () => { return ( <button className={s.button}>Click me</button> ); }; export default Button;
アプリケーションがコンパイルされると、レンダリングされた HTML は次のようになります。
<button class="Button_button__3FQ9Z">Click me</button>
この場合、 Button_button__3FQ9Z
、CSS モジュールから生成されたハッシュされたクラス名です。ハッシュの正確な構造と長さは、プロジェクトの構成によって異なる場合があることに注意してください。
この一意のクラス名により、 Button.module.css
で定義したスタイルがこのボタンにのみ適用され、アプリケーション内の他の要素には影響しません。また、ハッシュされたクラス名を明示的にターゲットにしない限り、他のスタイルがこのボタンに影響を与えることはできません。このスタイルのカプセル化は、CSS モジュールの主な利点の 1 つです。
CSS を処理するもう 1 つの一般的な方法は、 styled-componentsや情動などの CSS-in-JS ライブラリを使用することです。これらのライブラリを使用すると、JavaScript 内に CSS を直接記述することができ、これにはいくつかの利点があります。
React アプリケーションでスタイル付きコンポーネントを使用する方法の例を次に示します。
import React from 'react'; import styled from 'styled-components'; const Button = styled.button` color: white; background-color: ${(props) => props.primary ? 'blue' : 'gray'}; `; const App = () => { return ( <div> <Button primary>Primary Button</Button> <Button>Secondary Button</Button> </div> ); }; export default App;
この例では、 Button
コンポーネントには、 primary
プロパティに基づいて変化する動的スタイルがあります。
CSS モジュールなどをサポートする JavaScript フレームワークを使用していない場合でも、 BEM (ブロック、要素、モディファイア) などの命名方法を使用して CSS を効果的に管理できます。
BEM は「Block Element Modifier」の略で、CSS で再利用可能なコンポーネントとコード共有を作成するのに役立つ方法論です。BEM を使用して CSS を構造化する方法は次のとおりです。
/* Block */ .top-menu { } /* Element */ .top-menu__item { } /* Modifier */ .top-menu__item_active { }
BEM では、「ブロック」はそれ自体で意味のあるスタンドアロンのエンティティであり、「要素」はスタンドアロンの意味を持たず、意味的にそのブロックに関連付けられているブロックの一部であり、「修飾子」はフラグです。外観や動作を変更するために使用されるブロックまたは要素。
BEM のような一貫した方法論を使用すると、特に大規模なプロジェクトにおいて、CSS の理解と保守が容易になります。
CSS モジュールや CSS-in-JS ライブラリから BEM のような命名方法まで、大規模なプロジェクトで CSS を管理する方法はいくつかあります。重要なのは、チームやプロジェクトに適したアプローチを見つけて、それを一貫して適用することです。 CSS を書くということは、理解しやすく保守しやすいコードを書くことであるのと同じくらい、効率的でパフォーマンスの高いコードを書くことでもあるということを忘れないでください。
コーディングを楽しんでください!