
気づいたらCSS、めちゃくちゃ強くなってませんか。
「それSassじゃなくて生CSSでよくね?」みたいな場面が増えてきたので、ここ数年のあいだに実務レベルで普通に使えるようになったテクニックをまとめておきます。
カスタムプロパティ、aspect-ratio、最新擬似要素など、ベテランほど見逃している便利CSSを一気に整理します。
1. カスタムプロパティ var() :ネイティブCSS変数
まずは定番。CSSの中で変数を定義して再利用できる仕組みです。ファイルの上位で定義しておけば、以降変数として同じ値を使い放題。「あのカラーコードなんだっけ?」という記憶検索がなくなる超便利機能。これなくしてもはや開発はできません。
:root {
--color-primary: #2563eb;
--radius-md: 0.75rem;
}
.button {
background: var(--color-primary);
border-radius: var(--radius-md);
}
実務での使いどころ
テーマカラー・余白・角丸・影などをトークン化
ダークモードやブランドテーマの切り替え
JSからテーマを動的変更
Sassの $primary しか知らないままだと損。今のCSS設計の土台になっています。
2. calc() / min() / max() / clamp() :フルードな値計算
CSS内で数式を扱える機能たち。
①calc():とにかく「足し算・引き算・掛け算・割り算」したいとき
異なる単位同士を数式で組み合わせられます。
.main {
width: calc(100% - 2rem);
}
.column {
width: calc(50% - 8px);
}
% + px / rem + vw
みたいな「普通なら一発指定できない」組み合わせを計算してくれます。
② min():小さい方を採用する
「この2つ(以上)の値のうち、小さい方を使って」と指定する。
.main {
width: min(100%, 960px);
}
画面が狭い時:100% が小さい ⇒ 100%
画面が広い時:960px が小さい ⇒ 960px
→ 「最大幅960pxだけど、狭い画面では素直に100%」というお決まりレイアウトが一行で書ける。
よくある使い方
.container {
width: min(100% - 2rem, 1200px);
}
③. max():大きい方を採用する
min()の逆で、「この中の大きい方の値を使う」。
.sidebar {
width: max(240px, 20vw);
}
画面が狭い時:240px が大きい ⇒ 240px
画面が広い時:20vw が大きい ⇒ 画面に応じて拡大
→ 「絶対に小さすぎてほしくないけど、画面が広ければもっと広くしていい」みたいな指定ができる。
④. clamp():最小値・理想値・最大値の三点セット
clamp(最小値, 理想値, 最大値) の形で、「この理想値で動いていいけど、
ここより小さくも大きくもなるなよ」と三点縛りができるやつ。
h1 {
font-size: clamp(1.8rem, 1.2rem + 2vw, 3rem);
}
画面が狭い時でも 1.8rem 未満にはならない
画面が広くなるにつれ 1.2rem + 2vw で伸びる
どれだけ広くしても 3rem を超えない
とか、「デザイナーのわがまま」をきれいに式に落とせるようになりました。
3. aspect-ratio ― ダミー要素いらずのアスペクト比固定
要素の縦横比を一発で指定できます。
.card-thumb {
aspect-ratio: 16 / 9;
object-fit: cover;
}
以前やっていたような、
.thumb::before {
content: "";
display: block;
padding-top: 56.25%; /* 16:9 */
}
みたいな黒魔術を書く必要がほぼなくなります。
レイアウトシフト(CLS)対策としても優秀で、画像がまだ来てなくても枠だけ先に確保してくれます。
4. :is() ― 長いセレクタの共通部分をまとめる糖衣構文
複数セレクタをひとまとめにして書ける疑似クラスです。
.article :is(h1, h2, h3, h4) {
margin-block: 1.5rem 0.5rem;
}
以前だと、
.article h1,
.article h2,
.article h3,
.article h4 { … }
と書いていたところを、共通部分を前にひとつだけ書けるのがポイント。
5. :where() ― 特異性ほぼゼロでベーススタイルを敷く
:is() と似ていますが、特異性(セレクタの強さ)がほぼ0になるバージョン。
:where(h1, h2, h3) {
font-family: system-ui, sans-serif;
margin-block: 0 0.5em;
}
ベーススタイル用にガンガン使ってOK。
あとから .page-title などで簡単に上書きできるので、リセット/ノーマライズ/ベースCSSとの相性がいいテクニックです。
6. :has() ― ついに来た「疑似・親セレクタ」
子や中身の状態に応じて「親」をスタイルできる、本命機能。
/* 画像を含む .card だけパディング調整 */
.card:has(img) {
padding-top: 0;
}
/* 不正な入力がある form を枠線赤に */
form:has(:invalid) {
border: 1px solid red;
}
使い例
/* チェックボックスONのときラベル背景を変える */
label:has(input[type="checkbox"]:checked) {
background: #e0f2fe;
}
/* 現在ページのリンクを含むナビだけを強調 */
.nav-item:has(.is-current) {
font-weight: 700;
}
今までJSで closest() をゴリゴリ書いていたところが、CSSだけで完結するようになってきています。
7. :focus-visible ― マウスとキーボードのフォーカスを出し分ける
マウスクリック時はフォーカスリングを隠したいけど、キーボード操作のときはちゃんと見せたい――というワガママを叶える疑似クラス。
button {
outline: none;
}
button:focus-visible {
outline: 2px solid #2563eb;
outline-offset: 2px;
}
これでアクセシビリティを犠牲にせずに「変な青枠」を消せるようになります。
8. @supports ― CSSだけで機能検出する
新しいCSS機能を「使えるブラウザだけで使う」ための条件分岐。
@supports (aspect-ratio: 1 / 1) {
.thumb {
aspect-ratio: 1 / 1;
}
}
コンテナクエリ、subgrid、新しいビューポート単位など、導入したいけど一部古い環境も面倒見たいというときに、プログレッシブエンハンスの軸になります。
9. prefers-reduced-motion / prefers-color-scheme ― ユーザー設定を読むメディアクエリ
動きを減らす prefers-reduced-motion
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
}
}
アニメーション酔いしやすいユーザーに配慮しつつ、通常ユーザーには今まで通りのリッチな動き、という設計ができます。
ダーク・ライトを切り替える prefers-color-scheme
@media (prefers-color-scheme: dark) {
body {
background: #020617;
color: #e5e7eb;
}
}
OSやブラウザのテーマ設定に自然に追従できるので、「ダークモードボタン作るほどじゃないけど対応はしたい」レベルのサイトと相性が良いです。
10. accent-color ― ネイティブフォームのブランド対応
チェックボックス、ラジオボタン、レンジスライダーなどのアクセントカラーを一発で変更できます。
input[type="checkbox"],
input[type="radio"],
input[type="range"] {
accent-color: #16a34a;
}
わざわざカスタムUIで差し替えなくても、
ネイティブ要素のアクセシビリティを保ったままブランドカラーを適用できるのが美味しいポイント。
11. scroll-behavior: smooth ― 全体のスクロール挙動を一括コントロール
html {
scroll-behavior: smooth;
}
ページ内リンク(<a href=”#section-3″>)や window.scrollTo を
自動でスムーズスクロールにしてくれる指定です。
prefers-reduced-motion: reduce のときには無効化してあげると、さらに丁寧。
12. スクロールスナップ scroll-snap-* ― スライダーの「カチッ」をCSSだけで
カルーセルや横スクロールUIを「ピタッ」と止める仕組み。
.carousel {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
.carousel > div {
flex: 0 0 100%;
scroll-snap-align: start;
}
JSでいちいち位置計算しなくても、
スマホの「横フリックで1枚ずつ送る」体験をかなりいい感じに再現できます。
13. overscroll-behavior ― モーダル内スクロールの「背面暴走」を止める
モーダルやサイドバー内部でスクロールしたとき、
勢い余って背面のページまでスクロールしてしまうアレを防ぐ指定。
.modal-body {
max-height: 70vh;
overflow: auto;
overscroll-behavior: contain;
}
サイドメニュー・チャットウィンドウ・埋め込みパネルなど、
「中だけスクロールさせたい」箇所すべてに効くので、地味に優先度高めのテクです。
14. フレックスレイアウトでも gap が使えるようになった
display: flex でも gap が普通に効く時代。
.nav {
display: flex;
gap: 1rem;
}
これで、子要素に margin-right を入れて、最後の要素だけ0にする
:not(:last-child) を駆使する
みたいな古のマージントリックから解放されます。
ラップするフレックスレイアウト(flex-wrap: wrap)とも相性◎。
15. 論理プロパティ ― margin-inline / padding-block など
書字方向に依存したプロパティで、
「上下」と「左右」を論理的な方向で表現します。
.card {
padding-block: 1.5rem; /* 上下 */
padding-inline: 1.25rem; /* 左右 */
margin-block-end: 2rem; /* 下側のmargin */
}
LTR/RTLや縦書きなどを見据えるなら、
今から書くCSSは top/bottom/left/right よりも
block/inline 系で組んでおくのがおすすめです。
16. コンテナクエリ @container ― コンポーネント単位のレスポンシブ
ブレイクポイントを「画面幅」ではなく「コンポーネントの幅」で決められる、かなり革命的な機能。
.card {
container-type: inline-size; /* カード自身の横幅に反応する */
}
@container (min-width: 480px) {
.card-inner {
display: grid;
grid-template-columns: 1fr 1.5fr;
gap: 1rem;
}
}
同じ .card をメインカラムでもサイドバーでも使える
親コンテナの幅に応じて「1カラム↔2カラム」切り替え
レイアウト崩れをコンポーネント単位で解決可能
「コンポーネント指向のレスポンシブ」を本気でやるなら、ここは押さえておきたいところ。
17. subgrid ― ネストしたグリッドで親のトラックを継承
グリッドレイアウトの中で、子グリッドが親と同じ列・行トラックを共有できる機能です。
.layout {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 1rem;
}
.article {
display: grid;
grid-template-columns: subgrid;
grid-column: 1 / -1;
}
ヘッダー・本文・フッターなどで、
「全体として同じグリッドに乗っている」感を出したいときに便利。
今までは各所で grid-template-columns をコピペするしかなかった領域を、ちゃんとDRYにできるようになります。
18. ネイティブCSSネスト ― Sass風の入れ子書きがそのままブラウザで動く
今まではビルドが必要だった「ネストされたCSS」が、いよいよネイティブで書けるようになりました。
.card {
padding: 1.5rem;
border-radius: 0.75rem;
& h2 {
margin-bottom: 0.5rem;
}
& a {
text-decoration: underline;
&:hover {
text-decoration: none;
}
}
}
Sassのネストと完全に同じではないので(ルールの書き方など一部仕様が違う)、
設計に使う前に一度ドキュメントを軽く眺めておくと安心です。
とはいえ、「コンポーネント単位でスタイルブロックを書く」スタイルがそのままCSSで完結するのはかなり大きい変化です。
19. カスケードレイヤー @layer ― 特異性バトルを終わらせる
CSSを「レイヤー」に分割し、
どのレイヤーがどれより強いか を明示できる仕組みです。
@layer reset, base, components, utilities;
@layer reset {
* { margin: 0; padding: 0; }
}
@layer base {
body { font-family: system-ui, sans-serif; }
}
@layer components {
.btn {
padding: .5rem 1rem;
border-radius: .375rem;
}
}
@layer utilities {
.mt-4 { margin-top: 1rem; }
}
ライブラリ側CSSを
@layer third-party {
/* ライブラリのスタイル */
}
自作コンポーネントを @layer components に置いておけば、
意味不明な特異性インフレを起こさずに、確実に上書きできます。
20. 新しいビューポート単位 svh / lvh / dvh ― モバイルのアドレスバー問題に終止符
従来の 100vh は、
スマホブラウザのアドレスバー出たり消えたりで、
「意図せずはみ出る・足りない」という問題がありました。
それをちゃんと扱うための単位がこちら:
svh(small viewport height)
lvh(large viewport height)
dvh(dynamic viewport height)
.hero {
min-height: 100svh; /* “今見えている”高さベース */
display: grid;
place-items: center;
}
ヒーローセクションやフルスクリーンモーダルなど、
画面高さピッタリに合わせたいUIを作るときは、
今後は 100vh ではなくこっちを検討した方が安全です。
まとめ ― これからのCSS設計の「優先採用枠」
いかがでしょうか?ここまで20個紹介しましたが、いきなり全部はさすがにしんどいので、
実務での優先度別にざっくり整理するとこんな感じになりそうです。
まずは絶対使っていきたい
カスタムプロパティ var()
gap(flex / grid)
論理プロパティ(margin-inline, padding-block など)
aspect-ratio
clamp() 系の値計算
新規案件では積極採用したい
:focus-visible
accent-color
:is() / :where()
scroll-behavior, overscroll-behavior
ネイティブCSSネスト
@layer(レイヤー設計)
プロジェクト単位で導入を検討したい
コンテナクエリ @container
新ビューポート単位 svh / lvh / dvh
:has()(親セレクタ的用途)
subgrid
スクロールスナップ
@supports
prefers-* 系メディアクエリ
「プリプロセッサありきのCSS」から、ブラウザがここまでやってくれるなら、まず生CSSで設計してから足りないところだけSassという発想に切り替えると、だいぶ世界観が変わります。
もしこの記事を読んで、
「自分のテーマやコンポーネント設計にどう落とし込むか」を詰めたくなったら、今書いているCSSを持ってきてください。いっしょにリファクタやりましょう!

岡崎龍夫
Eclo編集長。デザイナー、エンジニア、コンサルタント、ライターなど様々な職域を持つがもともとは俳優。表方の舞台活動から裏方のアートマネジメントに移行し、都内で舞台のプロデュースを手がけてるうちに、デザイン、WEB開発、マーケティングといった職能を身につける。趣味はDJとアザラシ探訪。現在は合同会社elegirlを設立し、WEBの企画、開発、コンサルティングを中心に活動している。