CSS Protips
CSSスキルをプロレベルに引き上げるためのヒント集です。各項目は自己完結型のパターンであり、直面している問題に適したものを適用してください。
CSSリセットを使用する
CSSリセットは、ブラウザ間のスタイルの一貫性を強制し、要素をスタイリングするためのクリーンな状態を提供します。数多くのリセットパターンがありますが、より簡略化されたリセットアプローチを使用することもできます:
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
これで要素はマージンとパディングが削除され、box-sizingを使用してCSSボックスモデルでレイアウトを管理できるようになります。
デモ
[!TIP] 以下の
box-sizingを継承するヒントに従う場合は、CSSリセットにbox-sizingプロパティを含めないことを選択しても構いません。
box-sizingを継承する
box-sizingをhtmlから継承させます:
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
これにより、他の動作を利用するプラグインや他のコンポーネントでbox-sizingを変更することが容易になります。
デモ
コンポーネントリセットにはall: unsetを注意して使用する
要素のプロパティをリセットする場合、各プロパティを個別にリセットできます:
button {
background: none;
border: none;
color: inherit;
font: inherit;
outline: none;
padding: 0;
}
あるいは、allショートハンドとunsetを使用します。all: unsetはほとんどのプロパティをリセットします(unicode-bidi、direction、カスタムプロパティは除く)。継承されるプロパティはinheritに、その他はinitialに戻ります。<button>はdisplay: inlineになり、ネイティブのボックスを失うため、リセット後は必ずレイアウトとフォーカスを復元してください。
button.reset {
all: unset;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
button.reset:focus-visible {
outline: 2px solid currentColor;
outline-offset: 0.15em;
}
[!TIP] 空白の状態ではなくユーザーエージェントスタイルシートを復元するには、
all: revertを使用します。モダンCSSセクションのリセットの意図:unsetvsrevertを参照してください。
ナビゲーションで:not()を使用してボーダーを適用/解除する
ボーダーを設定して...
/* ボーダーを追加 */
.nav li {
border-right: 1px solid #666;
}
...そして最後の要素からそれを削除する代わりに...
/* ボーダーを削除 */
.nav li:last-child {
border-right: none;
}
...:not()擬似クラスを使用して、必要な要素にのみ適用します:
.nav li:not(:last-child) {
border-right: 1px solid #666;
}
ここでは、CSSセレクタが人間が説明するように読めるようになります。
デモ
フォントがローカルにインストールされているか確認する
フォントをリモートから取得する前に、ローカルにインストールされているかどうかを確認できます。これはパフォーマンスのヒントとしても優れています。
@font-face {
font-family: "Dank Mono";
src:
/* フルネーム */ local("Dank Mono"), /* ポストスクリプト名 */ local("Dank Mono"),
/* そうでなければ、ダウンロード! */ url("//...a.server/fonts/DankMono.woff");
}
code {
font-family: "Dank Mono", system-ui-monospace;
}
Adam Argyle氏に、このプロトタイプとデモを共有してくれたことに感謝します。
bodyにline-heightを追加する
各<p>、<h*>などに個別にline-heightを追加する必要はありません。代わりにbodyに追加します:
body {
line-height: 1.5;
}
これにより、テキスト要素はbodyから容易に継承できます。
デモ
フォーム要素に:focusを設定する
視覚的なキーボードユーザーは、キーボードイベントがページ内のどこに移動するかを判断するためにフォーカスに依存します。フォーム要素のフォーカスを、ブラウザのデフォルト実装よりも目立ち、一貫性のあるものにします:
a:focus,
button:focus,
input:focus,
select:focus,
textarea:focus {
box-shadow: none;
outline: #000 dotted 2px;
outline-offset: 0.05em;
}
デモ
[!TIP] モダンなコードでは、
:focus-visibleを優先して、キーボードユーザーが明確なフォーカスリングを維持し、マウスクリックごとにフォーカススタイルが表示されないようにします。
任意の要素を垂直方向に中央揃えする
モバイルでは動的ビューポートユニットを持つグリッドを優先します。100vhは大きなビューポートにマッピングされ、ブラウザのツールバーが表示/非表示になると誤動作する可能性があります。論理プロパティを使用すると、100dvb(動的ビューポートブロック)を使用します。
.center-page {
min-block-size: 100dvb;
display: grid;
place-items: center;
}
親に定義されたブロックサイズがあれば、Flexboxも同様に機能します:
html,
body {
min-block-size: 100%;
}
body {
align-items: center;
display: flex;
justify-content: center;
}
[!NOTE] レガシーパターン:
height: 100vh+place-items: centerは依然として古いチュートリアルで見られます。モバイルでのフルビューポートレイアウトには、dvh/dvb/svh/lvhを優先してください。ビューポート相対長についてはMDNを参照してください。
[!TIP] 他のものを中央揃えにしたいですか? CSS-Tricksに素晴らしい記事があります。
デモ
高さ/幅の代わりにaspect-ratioを使用する
aspect-ratioプロパティを使用すると、要素のサイズを簡単に設定し、一貫した幅と高さの比率を維持できます。これはレスポンシブWebデザインにおいて、レイアウトシフトを防ぐために非常に便利です。画像の高さ/幅の値が変更されてもレイアウトが崩れないように、object-fitと併用します。
img {
aspect-ratio: 16 / 9; /* 幅 / 高さ */
object-fit: cover;
}
aspect-ratioプロパティの詳細については、web.devの記事をご覧ください。
デモ
カンマ区切りのリスト
リストアイテムを実際のカンマ区切りリストのようにします:
ul > li:not(:last-child)::after {
content: ",";
}
:not()擬似クラスを使用すると、最後のアイテムにカンマが追加されません。
[!NOTE] このヒントは、特にスクリーンリーダーにとってアクセシビリティに適さない場合があります。また、ブラウザからのコピー/ペーストはCSS生成コンテンツでは機能しません。注意して使用してください。
負のnth-childを使用して項目を選択する
CSSで負のnth-childを使用して、1からnまでの項目を選択します。
li {
display: none;
}
/* 1から3までの項目を選択して表示 */
li:nth-child(-n + 3) {
display: block;
}
または、:not()の使用についてすでに学んだので、次を試してください:
/* 最初の3つを除くすべての項目を選択して表示 */
li:not(:nth-child(-n + 3)) {
display: block;
}
デモ
アイコンにSVGを使用する
アイコンにSVGを使用しない理由はありません:
.logo {
background: url("logo.svg");
}
SVGはすべての解像度タイプで適切にスケーリングされ、IE9までのすべてのブラウザでサポートされています。.png、.jpg、または.gif-jif-whatevファイルは廃棄してください。
[!NOTE] 視覚ユーザー向けにSVGアイコンのみのボタンがあり、SVGの読み込みに失敗した場合、これはアクセシビリティを維持するのに役立ちます:
.no-svg .icon-only::after {
content: attr(aria-label);
}
「ロボトミーアウル」セレクタを使用する
奇妙な名前かもしれませんが、ユニバーサルセレクタ(*)と隣接兄弟セレクタ(+)を組み合わせることで、強力なCSS機能を提供できます:
* + * {
margin-top: 1.5em;
}
この例では、ドキュメントのフロー内で他の要素に続くすべての要素は、margin-top: 1.5emを受け取ります。
[!TIP] 「ロボトミーアウル」セレクタの詳細については、A List Apart のHeydon Pickering氏の記事をお読みください。
デモ
純粋なCSSスライダーにmax-heightを使用する
オーバーフローを非表示にしてmax-heightを使用して、CSSのみのスライダーを実装します:
.slider {
max-height: 200px;
overflow-y: hidden;
width: 300px;
}
.slider:hover {
max-height: 600px;
overflow-y: scroll;
}
ホバー時に要素はmax-heightの値まで拡張され、オーバーフローの結果としてスライダーが表示されます。
等幅のテーブルセル
テーブルは扱いが面倒な場合があります。table-layout: fixedを使用してセルの幅を均等に保ちます:
.calendar {
table-layout: fixed;
}
手間のかからないテーブルレイアウト。
デモ
Flexboxでマージンハックを取り除く
カラムの溝を扱う場合、flexboxのspace-betweenプロパティを使用して、nth-、first-、last-childのハックを取り除くことができます:
.list {
display: flex;
justify-content: space-between;
}
.list .person {
flex-basis: 23%;
}
これで、カラムの溝は常に均等に配置されます。
空のリンクに属性セレクタを使用する
<a>要素にテキスト値がなく、href属性にリンクがある場合にリンクを表示します:
a[href^="http"]:empty::before {
content: attr(href);
}
これは非常に便利です。
デモ
[!NOTE] このヒントは、特にスクリーンリーダーにとってアクセシビリティに適さない場合があります。また、ブラウザからのコピー/ペーストはCSS生成コンテンツでは機能しません。注意して使用してください。
:is()で詳細度をより適切に制御する
:is()擬似クラスは、一度に複数のセレクタをターゲットにするために使用され、冗長性を減らし、コードの可読性を向上させます。これは、大きなセレクタをよりコンパクトな形式で記述するのに非常に役立ちます。
:is(section, article, aside, nav) :is(h1, h2, h3, h4, h5, h6) {
color: green;
}
上記のルールセットは、以下の多数のセレクタルールと同等です...
section h1,
section h2,
section h3,
section h4,
section h5,
section h6,
article h1,
article h2,
article h3,
article h4,
article h5,
article h6,
aside h1,
aside h2,
aside h3,
aside h4,
aside h5,
aside h6,
nav h1,
nav h2,
nav h3,
nav h4,
nav h5,
nav h6 {
color: green;
}
デモ
「デフォルト」リンクのスタイル設定
「デフォルト」リンクにスタイルを追加します:
a[href]:not([class]) {
color: #008000;
text-decoration: underline;
}
これで、CMSを介して挿入され、通常はclass属性を持たないリンクが、カスケードに汎用的に影響を与えることなく区別されるようになります。
固有の比率のボックス(レガシー代替)
[!NOTE] レガシーのみ。 新しいコードでは
aspect-ratio(Baseline Widely available)を優先します。非常に古いブラウザをサポートする必要がある場合、またはaspect-ratioよりも前のパターンが必要な場合にのみ、このパディングハックを保持してください。
固有の比率を持つボックスを作成するには、divに上部または下部のパディングを適用します:
.container {
height: 0;
padding-bottom: 20%;
position: relative;
}
.container div {
border: 2px dashed #ddd;
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
パディングに20%を使用すると、ボックスの高さは幅の20%になります。ビューポートの幅に関係なく、子divはアスペクト比を維持します(100% / 20% = 5:1)。
デモ
壊れた画像のスタイル設定
少しのCSSで、壊れた画像をより美しく見せることができます:
img {
display: block;
font-family: sans-serif;
font-weight: 300;
height: auto;
line-height: 2;
position: relative;
text-align: center;
width: 100%;
}
次に、疑似要素ルールを追加して、ユーザーメッセージと壊れた画像のURL参照を表示します:
img::before {
content: "We're sorry, the image below is broken :(";
display: block;
margin-bottom: 10px;
}
img::after {
content: "(url: " attr(src) ")";
display: block;
font-size: 12px;
}
[!TIP] このパターンのスタイリングの詳細については、Ire Aderinokun氏の記事をご覧ください。
グローバルサイジングにはremを、ローカルサイジングにはemを使用する
ルートで基本フォントサイズを設定した後(html { font-size: 100%; })、テキスト要素のフォントサイズをemに設定します:
h2 {
font-size: 2em;
}
p {
font-size: 1em;
}
次に、モジュールのフォントサイズをremに設定します:
article {
font-size: 1.25rem;
}
aside .module {
font-size: 0.9rem;
}
これで、各モジュールは区画化され、スタイル設定が容易になり、保守性が向上し、柔軟性が高まります。
ミュートされていない自動再生動画を非表示にする
これはカスタムユーザースタイルシートに最適なテクニックです。ページ読み込み時に自動再生される動画からのサウンドでユーザーに過負荷をかけないようにします。サウンドがミュートされていない場合は、動画を表示しません:
video[autoplay]:not([muted]) {
display: none;
}
ここでも、:not()擬似クラスを活用しています。
柔軟なタイポグラフィに:rootを使用する
レスポンシブレイアウトのタイプフォントサイズは、各ビューポートに合わせて調整できる必要があります。:rootを使用して、ビューポートの高さと幅に基づいてフォントサイズを計算できます:
:root {
font-size: calc(1vw + 1vh + 0.5vmin);
}
これで、:rootによって計算された値に基づいてroot emユニットを利用できます:
body {
font: 1rem/1.6 sans-serif;
}
デモ
モバイルでのエクスペリエンス向上のためにフォーム要素にfont-sizeを設定する
モバイルブラウザが<select>要素にフォーカスしたときに、iOS Safariがページを拡大表示するのを防ぐには、フォーム要素にfont-sizeを追加します:
input[type="text"],
input[type="number"],
select,
textarea {
font-size: 16px;
}
モダンCSS
これらのパターンは、モダンなブラウザ機能を活用しています。以下のMDN Baselineバッジは、ブラウザ間のサポートレベルを示しています。@supportsを使用したプログレッシブエンハンスメントにより、フォールバックを提供します。
[!IMPORTANT] Baselineはブラウザ互換性のみを測定します。アクセシビリティ、キーボードサポート、プリファレンス(モーション、カラースキーム)、コントラスト比は常に個別にテストしてください。新しく利用可能になった機能は、広く利用可能になるまで@supportsでラップします。
広く利用可能
これらの機能は主要ブラウザのすべての範囲で安定しており、追加のフォールバックなしで安全に使用できます。
focusより:focus-visibleを優先する
キーボードユーザーにはフォーカスリングを表示し、マウスクリックでは表示しないようにします。
/* キーボードフォーカスに対してのみフォーカススタイルを表示 */
:focus-visible {
outline: 2px solid var(--focus-color, Highlight);
outline-offset: 0.1em;
}
/* 習慣のために:focusをリセット */
:focus:not(:focus-visible) {
outline: none;
}
focus-visibleはBaseline 2023でWidely availableになりました。すべてのフォーカススタイルに対してデフォルトで使用してください。
論理プロパティと値
水平方向の物理的な値(left、right、top、bottom)ではなく、フロー相対的なショートカットを使用します。テキストの方向に関係なく、自動的に適応します。
/* 従来の物理的 */
.box { width: 100%; max-width: 600px; padding-left: 1em; }
/* フローに合わせて適応する論理的 */
.box { inline-size: 100%; max-inline-size: 600px; padding-inline-start: 1em; }
/* マージンとボーダーも同様 */
.element {
margin-block: 1em;
border-inline: 2px solid;
}
論理プロパティはBaseline 2023でWidely availableになりました。多言語レイアウトではデフォルトで使用してください。
カスタムプロパティ(CSS変数)
カスケード全体で値を再利用し、メディアクエリの切り替えを簡素化します。
:root {
--brand-color: #3366cc;
--gutter: clamp(1rem, 2vw, 2rem);
}
.button {
background: var(--brand-color);
padding-inline: var(--gutter);
}
/* ダークモードの切り替え */
@media (prefers-color-scheme: dark) {
:root {
--brand-color: #99bbff;
}
}
gapによるFlexbox
子マージンやラッパーdivを必要としない、シンプルなflexの溝。
.flex-grid {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.flex-grid > * {
flex: 1 1 200px;
}
クランプ、ミニ、マックス
clamp()、min()、max()を使用して、流体のタイポグラフィとレスポンシブな溝を実現します。
h1 {
font-size: clamp(1.5rem, 4vw, 3rem);
}
.container {
padding-inline: max(1rem, (100vw - 60ch) / 2);
}
Scroll Margin / Padding
目的のセクションがナビゲーションバーの後ろに隠れるのを防ぐために、スクロールアンカーを追加します。
[id] {
scroll-margin-top: 2rem;
}
prefers-reduced-motion
前庭障害を持つユーザーのためにアニメーションを無効にします。
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
リセットの意図: unset vs revert
unsetはユーザーエージェントスタイルをクリアします。revertはUAスタイルを復元します。
/* UAスタイルを保持したまま、要素の外観をリセット */
button.unstyled {
all: revert; /* UAのデフォルトに戻す */
cursor: pointer; /* ポインタは維持 */
}
revertはBaseline 2024でWidely availableになりました。
新しく利用可能
これらの機能は最近主要ブラウザ全体に展開されました。使用法を@supportsでラップするか、実用的なフォールバックを提供してください。
@propertyによる登録済みカスタムプロパティ
カスタムプロパティにタイプ、初期値、継承動作を定義します。アニメーションやグラデーションの切り替えに不可欠です。
@property --gradient-angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.box {
background: linear-gradient(var(--gradient-angle), red, blue);
transition: --gradient-angle 0.3s;
}
.box:hover {
--gradient-angle: 45deg;
}
Scroll Shadows(background-attachmentによる)
コンテナがオーバーフローしたときに、視覚的な手がかりとして影を表示します。
.scroll-container {
overflow-y: auto;
background:
/* 影のカバー */ linear-gradient(#fff 30%, transparent) 0 0 / 100% 50px,
/* 影 */ radial-gradient(ellipse at 50% 0%, rgba(0,0,0,.3) 0%, transparent 70%) 0 0 / 100% 15px;
background-repeat: no-repeat;
background-attachment: local, scroll;
}
:has()(親セレクタ)
子要素の状態に基づいて親要素をスタイリングします。
/* エラーアイコンを含むラベルをハイライト */
label:has(+ input:invalid) {
color: red;
}
/* 説明文付きのカードにのみグリッドを使用 */
.card:has(.description) {
display: grid;
grid-template-rows: auto 1fr;
}
/* フォームグループのパターン */
.field:has(input[required]) .label::after {
content: "*";
color: red;
}
Content-visibility
オフスクリーン要素のレンダリングを延期して、初期読み込みを高速化します。
.section {
content-visibility: auto;
contain-intrinsic-size: 500px; /* スクロールサイズのプレースホルダー */
}
[!CAUTION] 隠されたコンテンツはアクセシビリティツリーのクエリに応答しない場合があります。検索可能にするために
content-visibility: visibleのフォールバックを含めてください。
Container Queries
ビューポートではなく、親コンテナのサイズに基づいてスタイリングします。
/* コンテナを定義 */
.card-container {
container-type: inline-size;
container-name: card;
}
/* コンテナの幅に基づいてクエリ */
@container card (min-width: 400px) {
.card {
display: flex;
gap: 1rem;
}
}
prefers-contrast
ユーザーがコントラストのプリファレンスを設定しているかどうかを検出します。
.button {
border: 1px solid gray;
}
@media (prefers-contrast: more) {
.button {
border-width: 3px;
}
}
@media (prefers-contrast: less) {
.button {
border-color: lightgray;
}
}
View Transitions API
シングルページアプリ(SPA)でビュー間のシームレスなアニメーションを有効にします。
/* ルートの遷移を定義 */
::view-transition-old(root) {
animation: fade-out 0.3s ease-out;
}
::view-transition-new(root) {
animation: fade-in 0.3s ease-in;
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
[!NOTE] View Transitionsはクロスドキュメント遷移を実験的にサポートしています(Chrome 126+でフラグ付き)。現時点では、SPAフレームワークでの使用に限定することを検討してください。
利用が制限されている
これらの機能は1~2つのブラウザに限定されています。使用する前に@supportsでテストし、フォールバックを提供してください。
@scope
セレクタのマッチングをDOMサブツリーに制限し、より予測可能なカスケードを実現します。
@scope (.card) {
:scope {
display: grid;
gap: 1rem;
}
p {
/* .card内のpのみに一致 */
margin: 0;
}
}
animation-timeline: scroll()
ページのスクロール位置に基づいてアニメーションを駆動します。
@property --progress {
syntax: "<number>";
inherits: false;
initial-value: 0;
}
.progress-bar {
animation: grow-progress linear;
animation-timeline: scroll(root block);
transform-origin: left;
}
@keyframes grow-progress {
from { scale: 1 0; }
to { scale: 1 1; }
}
text-wrap: pretty / balance
タイポグラフィのバランスを取り、孤語(widows/orphans)を排除します。
h1, h2, h3 {
text-wrap: balance; /* ヘッダーで使用 */
}
p {
text-wrap: pretty; /* パラグラフで使用 */
}
text-wrap: prettyはChrome 117+(フラグ付き)で利用可能です。balanceはChrome 114+で利用可能です。
light-dark()カラー関数
メディアクエリなしで、color-schemeに基づいてライトモードとダークモードの色を切り替えます。
:root {
color-scheme: light dark;
}
body {
background: light-dark(white, black);
color: light-dark(black, white);
}
[!NOTE] Chrome 123+、Firefox 120+でサポートされています。Safariでは未対応です。ダークモードのフォールバックを含めてください。
謝辞
元のCSS Protipsコレクションは、AllThingsSmitty/css-protipsに基づいています。このバージョンでは、MDN Baseline分類と、web.devおよびMDNで文書化されたモダンCSSパターンの追加が含まれています。