「これ、JavaScript書かないと無理だよね」——そう思っていた実装の多くが、2026年のCSSだけで完結するようになりました。親要素の判定、textareaの自動リサイズ、フェードイン、スクロール連動アニメーション。

どれも昔は jQuery や useEffect の出番でしたが、今はブラウザが標準で面倒を見てくれます。

この記事では、実際に手元でそのまま動く7つの新機能をコピペできるコード付きで紹介します。全部合わせると、あなたのプロジェクトから数百行のJSが消えるかもしれません。

独学に限界を感じたら

プログラミング教室がおすすめ。挫折せず最短で実力を伸ばせます。

PR

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-styletransition-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: balanceaccent-color のような「付けるだけで壊れない」ものから試してみてください。気づけば、あなたのコードベースからJavaScriptが静かに消えていくはずです。