「これ、JavaScript書かないと無理だよね」——そう思っていた実装の多くが、2026年のCSSだけで完結するようになりました。親要素の判定、textareaの自動リサイズ、フェードイン、スクロール連動アニメーション。
どれも昔は jQuery や useEffect の出番でしたが、今はブラウザが標準で面倒を見てくれます。
この記事では、実際に手元でそのまま動く7つの新機能をコピペできるコード付きで紹介します。全部合わせると、あなたのプロジェクトから数百行のJSが消えるかもしれません。
1. :has() —— ついに手に入った「親セレクタ」
CSSに20年なかった「親を選ぶ」機能。:has() は「〜を子孫(や隣接)に持つ要素」を選択します。これまで「画像を含むカードだけ余白を変える」ような処理はJSでクラスを付け外ししていましたが、もう不要です。
/* 画像を含むカードだけ上パディングを消す */
.card:has(img) {
padding-block-start: 0;
}
/* チェック済みのチェックボックスを持つラベルを強調 */
label:has(input:checked) {
font-weight: 700;
}
/* 不正な入力があるフォームは送信ボタンを無効化 */
.form:has(input:user-invalid) button[type="submit"] {
opacity: 0.5;
pointer-events: none;
}特に3つ目のパターンは強力で、「入力を監視して送信ボタンの活性/非活性を切り替える」という定番のJSがまるごと消えます。:has() は Chrome / Safari / Firefox 全てで安定して使えます。
2. field-sizing: content —— 入力に合わせて伸びるtextarea
チャットUIでおなじみの「入力量に応じて高さが伸びるtextarea」。これまでは scrollHeight を測って高さを再設定するJSが定番でしたが、field-sizing: content の1行で置き換えられます。2026年6月に全モダンブラウザで Baseline 入りした、まさに今旬の機能です。
textarea {
field-sizing: content; /* 中身に合わせて自動リサイズ */
min-height: 3lh; /* 最低3行ぶんは確保 */
max-height: 12lh; /* 12行を超えたらスクロール */
}
/* input にも効く(検索ボックスなどが中身に応じて伸縮する) */
input[type="text"] {
field-sizing: content;
}lh は「1行の高さ」を表す単位。min-height / max-height と組み合わせれば、リサイズの範囲まで宣言的に指定できます。onInputハンドラでの高さ計算とはお別れです。
3. popover 属性 —— モーダルもメニューもHTMLだけで
ドロップダウン、ツールチップ、モーダル。これらの「開閉」「他をクリックしたら閉じる」「Escで閉じる」「最前面に重ねる」を、属性2つで実現できます。JSのイベントリスナは不要です。
<button popovertarget="menu">メニューを開く</button>
<div id="menu" popover>
<a href="/">ホーム</a>
<a href="/about">このサイトについて</a>
</div>popovertarget でトグルボタンを、popover 属性で中身を指定するだけ。背景クリックやEscキーでの自動クローズ、top-layer への重ね合わせ(z-index地獄からの解放)まで、すべてブラウザが担当します。
4. @starting-style —— display:none からのフェードインが素直に書ける
「非表示の要素をふわっと出す」——長年トランジションを苦しめてきたのが display: none でした。
@starting-style と transition-behavior: allow-discrete を使えば、出現アニメーションが純CSSで完結します。先ほどのpopoverと組み合わせてみましょう。
[popover] {
opacity: 0;
transition:
opacity 0.25s ease,
display 0.25s allow-discrete, /* display の切り替えもアニメ対象に */
overlay 0.25s allow-discrete; /* top-layer からの離脱も遅延 */
}
/* 開いている間の状態 */
[popover]:popover-open {
opacity: 1;
}
/* 表示され始めた“最初のフレーム”の初期値 */
@starting-style {
[popover]:popover-open {
opacity: 0;
}
}ポイントは3つの状態を宣言すること。「閉じているとき(opacity:0)」「開いているとき(opacity:1)」「開き始めの初期値(@starting-style)」。これでJSタイマーやアニメーション終了検知のコールバックが不要になります。
5. スクロール駆動アニメーション —— IntersectionObserverが要らない
「スクロールで要素をふわっと表示」「ページ上部の読了プログレスバー」。定番のスクロール演出は IntersectionObserver や scroll イベントで書くのが常識でしたが、animation-timeline でCSSだけに移せます。
/* 要素が画面に入ってきたらフェードイン */
@keyframes reveal {
from { opacity: 0; transform: translateY(2rem); }
to { opacity: 1; transform: translateY(0); }
}
.reveal {
animation: reveal linear both;
animation-timeline: view(); /* 自分の可視範囲を時間軸に */
animation-range: entry 0% cover 40%; /* 入り始め〜40%見えるまでで再生 */
}
/* ページ全体のスクロール進捗バー */
@keyframes grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.scroll-progress {
transform-origin: left;
animation: grow linear;
animation-timeline: scroll(root); /* ルートのスクロール量を時間軸に */
}view() は「その要素がビューポートに出入りする進捗」、scroll() は「スクロールコンテナの進捗」を時間軸(タイムライン)に変換します。Chrome / Safari で利用可能。Firefox はまだ実装中なので、次項の @supports でのフォールバックとセットで使うのがおすすめです。
6. text-wrap: balance / pretty —— 見出しの改行が美しくなる
見出しの2行目に単語が1つだけ残る「泣き別れ」。デザイナーに指摘されがちなこの問題も、CSS1行で解決します。
/* 見出しは各行の文字数を均等に割り付ける */
h1, h2, h3 {
text-wrap: balance;
}
/* 本文は最終行が1単語だけ残る“孤児”を防ぐ */
p {
text-wrap: pretty;
}balance は行ごとの文字数を均等化、pretty は最終行の孤立を避けつつ処理コストを抑えるモード。長い見出しほど効果が見た目に出ます。未対応ブラウザでは単に通常改行に戻るだけなので、気軽に付けて損なしのプロパティです。
7. おまけ:色とフォームの地味だけど効く3つ
最後に、知っておくとJSやSassを減らせる小ワザを3つ。
/* (a) color-mix(): 変数1つからホバー色を自動生成(Sass不要) */
.button {
--brand: #2563eb;
background: var(--brand);
}
.button:hover {
background: color-mix(in srgb, var(--brand) 85%, black);
}
/* (b) accent-color: チェックボックス/ラジオ/レンジの色を1行で */
:root {
accent-color: #2563eb;
}
/* (c) :user-invalid: 一度触れて不正になった入力だけ赤くする */
input:user-invalid {
border-color: #dc2626;
}:user-invalid は :invalid と違い「ユーザーが操作した後」にのみ効くため、ページ表示直後から未入力欄が真っ赤になる問題が起きません。バリデーション表示のためのJSフラグ管理が丸ごと不要になります。
安全に導入するために:@supports で段階的に
新機能を本番投入するときは、@supports による機能クエリでフォールバックを用意すると安心です。対応ブラウザだけが恩恵を受け、非対応環境は素の見た目に戻るだけ——という「壊れない」導入ができます。
/* :has() に対応している時だけ適用する例 */
@supports selector(:has(*)) {
.form:has(input:user-invalid) button {
opacity: 0.5;
}
}
/* スクロール駆動アニメに対応している時だけ適用 */
@supports (animation-timeline: scroll()) {
.reveal { animation: reveal linear both; animation-timeline: view(); }
}まとめ:CSSに任せられる仕事は、CSSに任せる
今回の7つを振り返ると、共通するのは「状態やレイアウトの判断をブラウザに宣言的に任せる」という発想です。JSで命令的に操作していた部分が減るほど、バグは減り、パフォーマンスは上がり、コードは読みやすくなります。
- :has() —— 親・状態に応じたスタイルをJSなしで
- field-sizing: content —— textarea自動リサイズ(2026 Baseline)
- popover 属性 —— モーダル/メニューをHTMLだけで
- @starting-style —— display切替のフェードインを純CSSで
- animation-timeline —— スクロール演出をIntersectionObserverなしで
- text-wrap: balance / pretty —— 改行を美しく
- color-mix / accent-color / :user-invalid —— 色とフォームの小ワザ
まずは text-wrap: balance や accent-color のような「付けるだけで壊れない」ものから試してみてください。気づけば、あなたのコードベースからJavaScriptが静かに消えていくはずです。





