Category Archive CSS・デザイン

ariaNotify()の危険性、ライブリージョンの泥沼を脱する方法

ariaNotify()の危険性、ライブリージョンの泥沼を脱する方法

Web制作者向けの新たなAPI、ariaNotify()の実装が進んでいる。これは開発者がJavaScriptから直接スクリーンリーダーの読み上げを制御できる機能だ。

一見すると非常に便利なAPIだが、CSS-Tricksの記事はその危険性に警鐘を鳴らす。使い方を誤れば、かつてのalert()のようにユーザー体験を損ねる「諸刃の剣」になり得るという。

この記事ではライブリージョンが抱えていた根本的な問題と、ariaNotify()がそれをどう解決するのかを解説する。その上で、実際の開発現場で陥りやすい誤用パターンと、責任ある実装のための考え方を示す。

ライブリージョンはなぜ「泥沼」だったのか

これまで動的なコンテンツ更新を支援技術に伝える手段は、ARIAライブリージョンしか存在しなかった。しかしこの仕組みには本質的な問題が山積している。

設計思想と実装の深刻なズレ

ライブリージョンとは、aria-live属性を付与した要素内で発生したDOMの変更を、スクリーンリーダーが自動的に読み上げる仕組みだ。値にassertiveを指定すれば即時割り込み、politeなら現在の読み上げ終了後に通知する。

理論上は理にかなっている。しかし現実には、ブラウザと支援技術の組み合わせごとに挙動が大きく異なる。特にライブリージョン内部にネストされたマークアップがある場合、期待通りの読み上げはほぼ保証されない。

開発者の理想(Before)
<div aria-live="polite">で囲めば自動読み上げされるはず
実際の開発現場(After)
ネストした要素のせいで読み上げ順が崩壊、display:noneからの復帰はタイミング問題で無視される
理想的な動作イメージ  実際に遭遇する問題

図のように、ライブリージョンはDOM変更の「通知」を目的として設計された。しかし現実には、ライブリージョンがDOM上に最初に出現したタイミングと、実際に読み上げたいコンテンツが挿入されるタイミングを緻密に制御しなければ、通知そのものが機能しない。

不可視の落とし穴がもたらす負債

より深刻なのは、これらの問題が「不可視」である点だ。視覚的なUIテストでは検出できず、スクリーンリーダーを使った専用のQAプロセスがなければ、読み上げの破綻に誰も気づかない。

多くの開発現場では、ライブリージョンを「通知用の簡易API」として誤用してきた。ページの奥に視覚的に非表示なライブリージョン要素を常駐させ、必要に応じてテキストを注入する手法だ。しかしこのアプローチでは、注入されたテキストがDOM上にゴミとして残り、スクリーンリーダーユーザーのページ探索を混乱させるリスクが常につきまとう。

ariaNotify()の仕組みと簡潔さ

ariaNotify()は、こうしたライブリージョンの苦行を根本から終わらせる。WAI-ARIA 1.3仕様で定義されたこのメソッドは、DOMの変更を一切必要とせず、直接スクリーンリーダーに読み上げ文字列を渡せる。

// 最もシンプルな呼び出し。デフォルトは優先度「normal」
document.ariaNotify("5件の新着メッセージがあります");
ariaNotify() の引数
第1引数 通知する文字列(必須)
第2引数 オプションオブジェクト。priority: "high" で即時割り込み通知に変更可能

デフォルトの優先度は"normal"で、これは従来のaria-live="polite"に相当する。現在の読み上げが終了するのを待ってから通知する。一方、priority: "high"を指定すればaria-live="assertive"のように即時割り込みが可能だ。

要素とドキュメントでの使い分け

このメソッドはElementインターフェースとDocumentインターフェースの両方で利用できる。両者に機能上の大きな差はないが、言語判定の挙動が異なる。

// Documentから呼び出した場合 → <html>のlang属性に従う
document.ariaNotify("送信が完了しました");

// 要素から呼び出した場合 → 最も近い祖先のlang属性に従う
buttonElement.ariaNotify("送信が完了しました");

この仕様により、多言語サイトでボタンごとに適切な言語で通知を出し分けることが可能になる。2026年6月現在、Firefoxで試験的に利用でき、JAWSやNVDAなど主要スクリーンリーダーが対応を進めている。

シンプルさが孕む危険性

シンプルさが孕む危険性

CSS-Tricksの記事で最も強調されているのは、このAPIの「扱いやすさ」こそが最大のリスクであるという点だ。著者はalert()関数との類似性を指摘し、強い警戒感を示している。

かつてのalert()が残した教訓

alert()は簡単に使えるがゆえに、1990年代から2000年代にかけて悪用され続けた。ページを開くたびに「最新情報があります」とダイアログが表示され、ユーザーの操作を強制的に中断する。今ではほとんどのブラウザが追加の抑制機能を設けている。

悪用された alert() のパターン(Before)
「今すぐ登録を!」というダイアログがページ読み込みのたびに出現
ariaNotify() の適切な使用(After)
ユーザーが能動的に操作した結果に対してのみ、簡潔なフィードバックを返す

ariaNotify()alert()と異なり、視覚的なダイアログを表示しない。しかしスクリーンリーダーユーザーにとっては、現在の読み上げを中断されるか否かという点で、本質的に同じ「割り込み」になり得る。

善意がノイズに変わる瞬間

最も警戒すべきは、開発者の「善意」が裏目に出るケースだ。コンテンツが表示されたときに「新しいコメントが追加されました」と通知する。ボタンにフォーカスしたときに「クリックするとメニューが開きます」と説明する。一見すると親切な実装だ。

しかしスクリーンリーダーユーザーは、すでに要素のセマンティクスやaria-expanded属性から、そのボタンがメニューを開くことや、コンテンツが展開されたことを理解している場合が多い。過剰な通知は単なるノイズであり、熟練ユーザーほど「チュートリアルを強制される煩わしさ」として体験する。

ARIAの三原則と責任ある実装

ARIAの三原則と責任ある実装

アクセシビリティの世界には「ARIA習得の三段階」と呼ばれる考え方がある。第一段階はARIAを使わない段階、第二段階はARIAを使い始める段階、第三段階は再びARIAを使わなくなる段階だ。

ネイティブHTMLで解決できるならそれを使え

W3Cの「ARIA利用の第一ルール」は明確だ。必要なセマンティクスと振る舞いがネイティブHTML要素で実現できるなら、ARIAで再発明してはならない。ariaNotify()もまた、この原則の例外ではない。

STEP 1 まずネイティブのHTML要素で要件を満たせるか検討する
STEP 2 aria-expandedや適切なセマンティクスで状態を表現できるか確認
STEP 3 それでも不足する場合に限り、ariaNotify()の使用を検討する

図で示した判断フローが重要だ。多くのケースでは、適切なセマンティクスとaria-expanded属性の組み合わせだけで、スクリーンリーダーは十分な情報をユーザーに提供できる。ariaNotify()は、これらのネイティブな手段ではどうしても伝えられない情報がある場合の「最終手段」として位置づけるべきだ。

ARIAの絶対性を理解する

ARIAには「解釈の余地」が存在しない。ブラウザと支援技術に対し、開発者が宣言した内容が絶対的な事実として伝達される。CSS-Tricksの記事はこの点を「私たちが言ったことがそのまま通る。交渉の余地はない」と表現する。

これは強力だが危険でもある。誤ったrole指定が見出し要素を単なるボタンに変えてしまうように、ariaNotify()の不用意な呼び出しは、ユーザーの操作フローを不可逆的に妨害する。そしてこの手の不具合は、スクリーンリーダーを用いたテストを実施しない限り、開発者が気づくことはない。

この記事のポイント

  • ariaNotify()はライブリージョンの煩雑さを解消する強力なAPIだが、その簡潔さゆえにalert()と同様の乱用リスクを孕む
  • Firefoxで先行実装されており、主要スクリーンリーダーが対応を進めている段階だ
  • 実装前にはネイティブHTMLと適切なセマンティクスで要件を満たせないか、必ず検討する必要がある
  • 通知はユーザーの能動的な操作に対するフィードバックに限定し、過剰な説明はノイズになる
  • スクリーンリーダーを用いたテストなしにリリースすれば、不可視の不具合として潜在し続ける
スクロール駆動アニメーションとスクロール状態、ビュー遷移の違いを整理

スクロール駆動アニメーションとスクロール状態、ビュー遷移の違いを整理

CSSのスクロール系アニメーションは混同しやすい概念が多い。スクロール駆動アニメーション、スクロールトリガーアニメーション、コンテナクエリのスクロール状態、そしてビュー遷移。いずれも「動き」を伴う機能だが、仕組みは大きく異なる。それぞれの動作を整理し、いつどの技術を使うべきかを明確にする。

CSS-Tricksの著者Geoff Graham氏も自身の記事で「言いたいことと意味することが違っていたり、必要な場面で別のものを選んでしまったりする」と述べており、開発者であれば誰もが直面する混乱だ。この記事では、4つの技術の本質的な違いをデモとともに確認していく。

スクロール駆動アニメーションとは

スクロール駆動アニメーションとは

スクロール駆動アニメーション(Scroll-Driven Animations)は、スクロールの進行状況とアニメーションの進行が直接連動する仕組みだ。ユーザーがスクロールバーを動かすとアニメーションが同じ比率で動き、スクロールを止めればアニメーションも止まる。逆方向にスクロールすれば、アニメーションも逆再生される。

これはつまり、スクロール位置がアニメーションのタイムラインを完全にコントロールすることを意味する。アニメーションの各フレームがスクロールの各ピクセルに紐づいているイメージだ。

スクロール駆動アニメーションの動作モデル
スクロール位置とアニメーション進行の関係
スクロール 0% → アニメーションも 0% 進行
スクロール 50% → アニメーションも 50% 進行
スクロール 100% → アニメーションも 100% 完了
双方向リンク:逆スクロールで逆再生

このデモで示すように、スクロール駆動アニメーションはスクロールという入力装置がそのままアニメーションの進行度を決める。ユーザーが任意の位置で停止できるし、前後にも動かせる。パララックス効果やプログレスバーなど、スクロールに完全に同期した視覚効果を作るのに向いている。

基本的なCSSの書き方

.element {
  animation: grow-progress linear forwards;
  animation-timeline: scroll();
}

animation-timeline: scroll() がキモだ。通常のアニメーションは時間ベースのタイムラインで動くが、これをscroll()にすることで、スクロール位置をタイムラインとして使えるようになる。これにより、スクロール量に応じて要素の透明度・サイズ・位置などを段階的に変化させられる。

スクロールトリガーアニメーションとは

スクロールトリガーアニメーションとは

スクロールトリガーアニメーション(Scroll-Triggered Animations)は、スクロール駆動と名前が似ているが、動作原理はまったく異なる。こちらはスクロール位置とアニメーション進行が連動しない。ある要素が特定のしきい値(トリガー活性化範囲)を越えた瞬間にアニメーションが発火し、最後まで一気に実行される。

スクロール駆動 vs スクロールトリガーの動作比較
スクロール駆動(Before)
スクロール量に比例 → 連続的に変化
※スクロール停止でアニメーションも停止
スクロールトリガー(After)
しきい値で発火 → 最後まで一気に実行
※発火後はスクロールに影響されない

トリガーは要素がスクロールポートに「入った」「出た」といったイベントだ。アニメーションが走り出してしまえば、その後のスクロール操作は関係ない。スクロール駆動アニメーションが「巻き戻せるビデオテープ」だとすれば、スクロールトリガーアニメーションは「一度押したら最後まで流れる再生ボタン」だ。

どんな場面で使うか

画面に要素が現れた瞬間にフェードインさせたり、スライドインさせたりするUI演出が代表的な用途だ。一度表示された後にスクロールで逆再生する必要がなければ、トリガー方式のほうがシンプルで意図が明確になる。逆にパララックスやプログレスインジケーターのように常時追従が必要な場合は、スクロール駆動を使う。

コンテナクエリのスクロール状態

コンテナクエリのスクロール状態

コンテナクエリのスクロール状態(Container Query Scroll State)は、CSS Conditional Rules Module Level 5のワーキングドラフトに含まれる比較的新しい概念だ。一言でいうと、コンテナが特定のスクロール条件に達したときにスタイルを更新する仕組みである。

スクロール駆動とスクロールトリガーの中間のような動きをする。スクロール位置に応じて何かが変わる点では駆動に近いが、アニメーションではなく「状態の切り替え」を扱う点が異なる。

.sticky-nav {
  container-type: scroll-state;
  position: sticky;
  top: 0;

  @container scroll-state(stuck: top) {
    background: orangered;
    border-radius: 0;
    flex-direction: row;
    width: 100%;

    a {
      text-decoration: none;
    }
  }
}
scroll-state の動作イメージ
通常時(sticky未発動)
ロゴ
メニュー 1(下線あり)
メニュー 2(下線あり)
縦並び・角丸・リンク下線あり
スクロールで上部固定時(stuck: top)
ロゴ
メニュー 1
メニュー 2
横並び・角丸ゼロ・背景色変更・リンク下線消去

この例では、position: stickyのナビゲーションがビューポート上部に張り付いた瞬間にレイアウトや配色が切り替わる。JavaScriptなしでスクロールに応じた状態管理をCSSだけで行える点が強力だ。ただし、2026年6月時点ではまだワーキングドラフト段階であり、ブラウザの対応状況を確認してから使う必要がある。

ビュー遷移(View Transitions)

ビュー遷移(View Transitions)

ビュー遷移は、ここまでの3つとは根本的に異なる。スクロールとは関係がない。CSSとJavaScriptが連携する完全なAPIで、ページ遷移や同一ページ内の状態変化をアニメーションさせる仕組みである。

同一ドキュメント遷移

同一ドキュメント遷移(Same-document transitions)は、ユーザー操作によって同一ページ上の要素がある状態から別の状態へ変化する際に使う。たとえばラジオボタンの選択状態が別の項目に移動するアニメーションや、グリッドビューからリストビューへの切り替えアニメーションなどが該当する。

クロスドキュメント遷移

クロスドキュメント遷移(Cross-document transitions)は、ページAからページBへ移動する際のアニメーションを実現する。デフォルトではクロスフェード(ページAが徐々に消え、ページBが徐々に現れる)で、実装も比較的簡単だ。複雑なエフェクトも可能で、たとえば円形のclip-pathで最初のページをワイプアウトしながら次のページを表示する、といった演出もできる。

クロスドキュメント遷移の流れ
遷移前(ページA)
🅰️ ページAのコンテンツ
遷移中(クロスフェード)
🅰️ 消えゆくページA → 🅱️ 現れつつあるページB
遷移後(ページB)
🅱️ ページBのコンテンツ

ビュー遷移の大きな利点は、MPA(マルチページアプリケーション)でもSPA(シングルページアプリケーション)のようなスムーズなページ遷移を実現できることだ。ChromeチームのBramus氏が多数の美しいデモを公開しており、実践的な実装例も増えている。

4つの技術をどう使い分けるか

4つの技術をどう使い分けるか

ここまでの整理を踏まえて、実務での使い分けを考える。選択の基準は「スクロールとアニメーションがどう関係するか」に尽きる。

4技術の目的別マッピング
パララックス・プログレスバー → スクロール駆動アニメーション
要素出現時のフェードイン → スクロールトリガーアニメーション
固定ヘッダーの状態切替 → コンテナクエリ スクロール状態
ページ遷移アニメーション → ビュー遷移

スクロール駆動はスクロール量に完全同期させたい場合、スクロールトリガーは「画面に入ったらポン」と動かしたい場合、スクロール状態は「ある条件下で見た目をパッと切り替えたい」場合、ビュー遷移は「ページ間や状態間をなめらかにつなぎたい」場合に選ぶ。

技術選定でよくある失敗は、スクロール駆動で十分な場面でトリガーを使ってしまったり、逆にトリガーで済む場面で複雑な駆動ロジックを書いてしまうことだ。要件に対してシンプルなほうを選ぶのが鉄則である。また、ビュー遷移をスクロール系の技術と混同しないことも重要だ。ビュー遷移はスクロールとは独立したレイヤーで動作するため、併用も可能だが別物として扱う必要がある。

この記事のポイント

  • スクロール駆動アニメーションはスクロール位置とアニメーション進行が双方向に連動する
  • スクロールトリガーアニメーションはしきい値で発火し、最後まで一気に実行される
  • コンテナクエリのスクロール状態は特定のスクロール条件でスタイルを切り替える(ドラフト段階)
  • ビュー遷移はスクロールとは無関係で、ページ遷移や状態変化をアニメーションさせるAPI
  • 実装前に「スクロールとどう関係するか」を明確にし、最もシンプルな技術を選ぶ
CSSの@functionルール入門。カスタム関数でスタイルを動的に管理

CSSの@functionルール入門。カスタム関数でスタイルを動的に管理

CSSに@functionというルールが導入されつつある。これはCSSで独自の関数を定義し、引数を受け取り、複雑な計算や条件分岐を経て値を返す仕組みだ。Sassの@mixinや@functionに似ているが、プリプロセッサを介さずにブラウザが直接解釈する点が新しい。

CSS-Tricksの記事によると、この機能は「CSS Custom Functions and Mixins Module Level 1」仕様の一部であり、カスタムプロパティ(変数)をさらに動的にした存在として位置づけられる。2026年6月現在は実験的機能だが、Chromeなど一部ブラウザでプレビューが始まっている。

本記事では@functionの基本構文、引数の扱い方、型チェック、カスケーディング、そして使用時の注意点までを整理する。

@functionとは何か、なぜ必要なのか

@functionとは何か、なぜ必要なのか

これまでCSSで動的な値を扱うにはカスタムプロパティ(--name)とcalc()の組み合わせが主だった。しかしcalc()だけでは条件分岐や複数ステップの演算が難しく、複雑な処理はSassに頼っていた。

@functionはこのギャップを埋める。CSSネイティブで「関数」を定義できるようになり、引数のバリデーション、デフォルト値、型チェック、メディアクエリに基づく分岐までを同一のスタイルシート内で完結させられる。

具体的には、色の透明度を動的に変えたり、画面幅に応じてフォントサイズを切り替えたり、複数の値をリストで受け取って最大値や最小値を計算したりといった処理が、プリプロセッサなしで可能になる。

従来のカスタムプロパティ(Before)
–base: 8px;
margin: calc(var(–base) * 2);
条件分岐や複数ステップの演算は不可
@functionルール(After)
@function –spacing(–n) { result: calc(8px * var(–n)); }
margin: –spacing(2);
引数と型チェック、デフォルト値、@media分岐も対応

このデモが示すように、@functionは単なる短縮記法ではなく、ブラウザのレンダリングエンジンが直接解釈するため、将来はパフォーマンス面でも恩恵が出ると期待されている。

基本構文を分解する

基本構文を分解する

関数の定義とresult記述子

@functionの基本形は以下のようになる。関数名はカスタムプロパティ同様、ダッシュ2つで始める必要がある。

@function --half(--size <length>) {
  result: calc(var(--size) / 2);
}

この例では--sizeという引数に<length>型を指定し、受け取った値を2で割った結果を返している。使用時は--half(20px)のように呼び出し、10pxが解決値となる。

result記述子は関数の戻り値を定義する必須要素だ。もしresultを書かないと、関数は常に「保証無効値(guaranteed invalid value)」を返し、実質的に機能しない。

returnsによる出力型の指定

returnsキーワードを使うと、関数が返す値の型を明示できる。これはバリデーションに役立つ。

@function --progression(--current <number>, --total <number>) returns <percentage> {
  result: calc(var(--current) / var(--total) * 100%);
}

上記では引数2つを数値型で受け取り、パーセンテージ型を返すことを宣言している。returnsを省略するとtype(*)相当となり、任意の型を受け入れる。

引数の型指定
–current <number>
数値のみ受け付ける。無効な値は関数呼び出し自体が無効化
戻り値の型指定
returns <percentage>
パーセンテージ以外が返るとブラウザが検知して無効化

こうした型チェックは大規模なコードベースでバグの早期発見に寄与する。JavaScriptのTypeScriptのように、CSSでも型安全性を確保できるわけだ。

複数型の許容とリスト引数

引数に複数の型を許容したい場合はtype()|を使う。

@function --transparent(--color <color>, --alpha type(<number> | <percentage>));

さらに、カンマ区切りで複数の値を1つの引数として受け取りたい場合、型の後ろに#を付与する。呼び出し側は波括弧{}で値を囲む。

@function --get-range(--list <length>#, --n <length>) {
  result: calc(max(var(--list)) - min(var(--list)) + var(--n));
}

div {
  padding-block: --get-range({10px, 100px, 50px, 25px}, 200px); /* 290px */
}

これはグラフのレンジ計算や、複数のスペーシング値から最適なマージンを導く際に便利だろう。

カスケードと条件分岐を活用する

カスケードと条件分岐を活用する

@functionのresultはCSSカスケードのルールに従う。つまり、複数のresultを記述した場合、最後にマッチした有効な値が採用される。

この性質を利用すれば、@media@container@supportsといった条件付きグループルールと組み合わせて、レスポンシブな値を関数内で完結できる。

@function --suitable-font-size() returns <length> {
  result: 16px;

  @media (width > 1000px) {
    result: 20px;
  }
}

body {
  font-size: --suitable-font-size();
}

この例では画面幅1000px以下なら16px、超えれば20pxを返す。従来はメディアクエリをスタイル宣言側で何度も書く必要があったが、関数に集約できるため保守性が向上する。

ただし、カスケードの「後勝ち」ルールには注意が必要だ。result: 16px;@mediaブロックより後に書くと、メディアクエリの結果に関わらず常に16pxが返る。記述順序を誤ると意図しない挙動になる。

ローカルスコープのカスタムプロパティ

@function内で定義したカスタムプロパティは、その関数内でのみ有効なローカルスコープとなる。グローバルに漏れ出さないため、命名の衝突を気にせず使える。

@function --spacing-scale(--multiplier) {
  --base-unit: 8px;
  result: calc(var(--base-unit) * var(--multiplier));
}

ここで定義された--base-unit--spacing-scaleの外部からは参照できない。大規模なCSS設計で名前空間の汚染を防ぐ手段として有効だ。

関数のネスト

ある@functionの中で別の@functionを呼び出すことも可能だ。これにより、単一責務の小さな関数を組み合わせて複雑な計算を構築できる。

@function --square(--n) {
  result: calc(var(--n) * var(--n));
}

@function --circle-area(--radius) {
  --pi: 3.14159;
  result: calc(var(--pi) * --square(var(--radius)));
}

.blob {
  width: calc(--circle-area(10) * 1px); /* 314.159px */
}
STEP 1 –circle-area が –radius を受け取る
STEP 2 内部で –square を呼び出して半径の2乗を計算
STEP 3 円周率を掛けて面積を返す

このネスト構造のおかげで、関数をレゴブロックのように組み立てられる。Sassで培われたモジュール設計の考え方を、そのままネイティブCSSに持ち込める未来が見える。

使用上の制約と注意点

使用上の制約と注意点

副作用の禁止

@functionは値を返すことだけが許されており、プロパティの変更や複数宣言の生成といった副作用は起こせない。これは関数型プログラミングの「純粋関数」に近い制約だ。

もし複数行のCSSプロパティをまとめて出力したい場合は、仕様策定中の@mixinルールを待つ必要がある。@functionと@mixinが揃えば、Sassの主要機能がCSSネイティブで再現されることになる。

循環依存の厳格な防止

CSSは循環参照に極めて厳しい。関数Aが関数Bを呼び、関数Bが関数Aを呼ぶような構造はブラウザが即座に検出し、両方の関数を無効化する。

これはカスタムプロパティの循環参照と同様の挙動だ。意図せず再帰を作り込まないよう、関数同士の呼び出し関係は明確に設計する必要がある。

循環依存の例
–func-a が –func-b を呼び出し
–func-b が –func-a を呼び出す
→ ブラウザが即座に両方を無効化
正しい依存関係
–func-a → –func-b → –func-c のように一方向の依存のみ
→ 安全に動作する

ブラウザサポートとプログレッシブエンハンスメント

2026年6月時点で@functionは実験的機能であり、未対応ブラウザでは単に無視される。そのため、フォールバックの記述が重要になる。

理想的には@supports (at-rule(@function))でサポートを判定したいが、この@supportsのat-rule評価機能自体がChrome 148以降でしか動作しないという皮肉な状況がある。

現実的な対策としては、従来のカスタムプロパティとcalc()で書いたスタイルをフォールバックとして先に宣言し、その下に@functionを使ったスタイルを記述する方法が考えられる。ブラウザが@functionを解釈できれば上書きされ、できなければ従来の記述が適用される。

/* フォールバック */
.container {
  margin-inline: 10px;
}

/* @function対応ブラウザでは上書き */
.container {
  margin-inline: --half(20px);
}

Sassの@functionとの違い

Sassの@functionとの違い

Sassにも同名の@functionがあるが、両者は動作の仕組みが根本的に異なる。Sassの@functionはコンパイル時にサーバーサイドで処理され、出力されるCSSには関数の痕跡が残らない。

一方、CSSネイティブの@functionはブラウザが実行時に解釈する。スタイルシート内に関数定義がそのまま存在し、ユーザーの画面幅やコンテナサイズに応じて動的に値が解決される。

この違いはパフォーマンス特性やデバッグのしやすさにも影響する。Sassの場合、コンパイル後のCSSしか確認できないが、ネイティブ@functionならブラウザのDevToolsで関数の解決過程を追えるようになる可能性がある。

Sassの@function
コンパイル時に処理され、静的な値としてCSSに出力される
実行時の再計算は不可、DevToolsで追跡困難
CSSネイティブの@function
ブラウザが実行時に解釈し、動的に値を解決する
メディアクエリに応じた再計算が可能、将来はDevTools対応も期待

なお、SassプロジェクトにCSSネイティブの@functionを混在させることは技術的には可能だが、同名の関数が競合する可能性があるため命名規則の整理が推奨される。

この記事のポイント

  • @functionはCSSネイティブでカスタム関数を定義するルールである
  • 引数、型チェック、デフォルト値、メディアクエリ分岐を1つの関数に集約できる
  • result記述子はカスケードに従い、複数記述時は後勝ちのルールが適用される
  • 副作用は禁止で、複数宣言の生成には@mixinの正式化を待つ必要がある
  • 循環依存は厳格に防止され、未対応ブラウザ向けのフォールバックが必須だ
2026年5月ウェブ標準 CSS疑似クラスや遅延読み込み新機能まとめ

2026年5月ウェブ標準 CSS疑似クラスや遅延読み込み新機能まとめ

2026年5月、Chrome 148、Firefox 151、Safari 26.5が安定版としてリリースされた。CSSの疑似クラスやコンテナクエリ、メディア要素の遅延読み込みといった使い勝手を大きく左右する機能群がBaselineへと加わっている。

具体的な変化点は4つだ。開閉状態にスタイルを当てる :open 疑似クラス、名前だけで親を参照できるコンテナクエリ、カスタムプロパティを条件とするスタイルクエリ、そして <video> および <audio> のネイティブ遅延読み込み。これらはすべて、主要ブラウザの最新版で動作するBaseline Newly availableとなった。

この記事では、それぞれの機能が何を解決し、実際のコードでどう使うのか、概観を交えながら見ていく。リリースノートを追いきれていないフロントエンドエンジニアやWeb制作担当者は、この機会にキャッチアップしてほしい。

:open 疑似クラスが Baseline 入り

:open 疑似クラスが Baseline 入り

長らくFirefoxとChromeが対応していた :open 疑似クラスが、Safari 26.5のサポートによりBaseline Newly availableとなった。開閉状態を持つHTML要素を、開いているときだけまとめてスタイリングできる疑似クラスだ。

具体的には <details><dialog><select> といった要素に加え、カラーピッカーや日付ピッカーなどの <input> も対象になる。これまでは details[open] のように属性セレクタで個別に書く必要があったが、より読みやすく一貫性のあるコードにまとめられる。

:open 疑似クラスが解決するもの

従来の手法では、開閉のインタラクションを表現するために要素ごとに異なるセレクタを書く必要があった。たとえば <details> には details[open]<dialog> には dialog[open] を使う。しかし :open 疑似クラスなら、これらをひとつのセレクタで扱える。

この変化は、コードのメンテナンス性を大きく改善する。とくにデザインシステムを構築するチームでは、コンポーネントの状態管理がシンプルになる利点が大きい。

従来の書き方(Before)
details[open] {
  background: #f0f0f0;
}

dialog[open] {
  background: #f0f0f0;
}
※要素ごとにセレクタを個別に書く必要があった
改善後の書き方(After)
:open {
  background: #f0f0f0;
}
※ひとつのセレクタで開状態を横断的に扱える

このデモで示したように、:open 疑似クラスを使うと開状態の記述が一箇所に集約される。複数箇所に散らばっていたスタイルを一元管理でき、意図しないスタイル崩れも防ぎやすくなる。

活用シーンと注意点

実務では、FAQのアコーディオンやモーダルダイアログのスタイル定義で即座に役立つ。フォーム部品のピッカーUIにも適用されるため、一貫したブランド表現が可能だ。

ただし、すべての開閉要素が対象になるわけではない。<summary> をクリックして開く <details> のように、ブラウザが開閉状態をネイティブに管理する要素に限定される。JavaScriptで独自に開閉を管理するUI部品には反応しない点に注意が必要だ。

名前のみのコンテナクエリ

名前のみのコンテナクエリ

Chrome 148のリリースにより、名前のみのコンテナクエリ(name-only container queries)がBaseline Newly availableになった。コンテナクエリを書く際に、サイズやスタイルの条件を省略してコンテナの「存在」だけを条件にできる。

これまでは container-type プロパティでコンテナの種別を宣言し、かつ @container ルール内でサイズ条件を指定する必要があった。今回の変更で、単に名前でコンテナを参照するだけのクエリが書けるようになり、コードの冗長さが大きく減る。

名前だけでコンテナを参照する仕組み

具体的なコードを見てほしい。従来は「サイドバーという名前のコンテナが、ある幅を超えたら」というクエリが中心だった。新しい構文では「サイドバーという名前のコンテナがあるなら」という条件だけでスタイルを切り替えられる。

/* コンテナの名前を付与 */
#container {
  container-name: --sidebar;
}

/* サイズ条件なしで名前だけで参照 */
@container --sidebar {
  .content {
    padding: 2rem;
  }
}
従来のコンテナクエリ(Before)
container-type: inline-size;
container-name: –sidebar;

@container –sidebar (min-width: 300px) { … }
※container-type とサイズ条件が必須だった
名前のみのコンテナクエリ(After)
container-name: –sidebar;

@container –sidebar { … }
※サイズ条件なしでコンテナの存在を参照できる

この構文によって、container-type の宣言が不要になるケースが増える。名前の指定だけでコンテナを参照したい場面では、CSSの記述量が減り、可読性も上がる。

実務での活用ポイント

大規模なサイトでは、レイアウトのコンポーネント化が進んでいる。コンテナクエリは「親コンポーネントの状態で子のスタイルを決める」設計と相性が良く、名前のみの参照はこの流れを加速する。

たとえば「サイドバーがDOM上に存在するならカードのパディングを変える」といった、レイアウトの有無に応じた条件分岐が簡潔に書ける。メディアクエリでは制御しきれなかったコンポーネント単位のレスポンシブが、より直感的に扱えるようになる。

コンテナスタイルクエリとカスタムプロパティ

コンテナスタイルクエリとカスタムプロパティ

Firefox 151で style() クエリのサポートが加わり、カスタムプロパティを条件とするコンテナスタイルクエリがBaseline Newly availableとなった。これにより、サイズ以外の親コンテナのCSSプロパティを条件にスタイルを切り替えられる。

とりわけ大きな意味を持つのは、カスタムプロパティ(CSS変数)を条件に含められる点だ。たとえば --theme という変数が dark のときに子要素の背景色を変える、といったテーマ切り替えをCSSだけで完結できる。

カスタムプロパティを条件にしたクエリの実例

以下は、親コンテナで定義された --theme: dark というカスタムプロパティを検知し、子の .card にダークモード用のスタイルを適用するコードだ。

@container style(--theme: dark) {
  .card {
    background-color: #1a1a1a;
    color: #fff;
  }
}
親コンテナ: –theme: light
カードタイトル
ライトテーマのカードコンテンツ
親コンテナ: –theme: dark(検知)
カードタイトル
ダークテーマのカードコンテンツ
–theme: light(検知対象外)  –theme: dark(styleクエリが発火)

この仕組みを使えば、JavaScriptに依存せずにテーマの切り替えが可能だ。CSS変数を用いた設計基盤が整っているプロジェクトであれば、追加のスクリプトなしでダークモード対応の精度を上げられる。

サイズクエリとの使い分け

サイズクエリは「コンテナの幅が300pxを超えたら」といったレイアウト制御に強い。一方でスタイルクエリは「テーマ」「状態」「モード」といった意味的な条件に向いている。

両者は競合するものではなく、補完関係にある。たとえば「サイドバーが存在し、かつダークテーマのとき」にスタイルを変える、といった複合的な条件設計も可能になる。コンテナクエリ全体がBaselineへと進んだことで、CSSの表現力は確実に一段階上がったと言える。

video と audio のネイティブ遅延読み込み

video と audio のネイティブ遅延読み込み

Chrome 148で <video><audio> 要素に loading="lazy" 属性が導入された。これにより、画像やiframeと同じく、メディア要素をビューポート付近まで読み込まない動作をブラウザネイティブで制御できる。

サイトのファーストビューに動画を置くケースは増えているが、初期ロードでユーザーの帯域を圧迫する問題は長年の課題だった。この機能はJavaScriptのIntersection Observerを使った手動実装を、宣言的な属性ひとつで置き換える。

実装と効果

実装はきわめてシンプルで、<video> タグに loading="lazy" を追加するだけだ。特別なポリフィルやライブラリは不要で、対応ブラウザであればそのまま動作する。

<video src="hero.mp4" loading="lazy" controls></video>
loading=”lazy” なし(Before)
ページ読み込み直後 すべての動画リソースを先読み
※帯域を圧迫し、LCPが悪化する
loading=”lazy” あり(After)
ビューポート接近時 必要なタイミングで読み込み開始
※初期ロードの負荷が下がり、帯域を節約できる

効果は数値にも表れる。Squarespaceのエンジニアリングチームが公開した記事によれば、ネイティブ遅延読み込みによって動画の初期リクエスト数が大幅に減り、LCP(Largest Contentful Paint)の改善に貢献したという。詳細は同チームの技術記事「How To Use Standard HTML Video and Audio Lazy-Loading on the Web Today」を参照してほしい。

対応範囲と今後の展望

Chrome 148でサポートが始まったこの機能は、今後のブラウザ展開によってBaseline化が期待される。FirefoxやSafariの動向はまだこれからだが、loading="lazy" の属性自体は画像やiframeですでに確立された仕組みであり、メディア要素への拡張も自然な流れと言える。

未対応ブラウザでは属性が無視されるだけで壊れないため、今すぐ実装してもリスクは少ない。動画を多用するポートフォリオサイトやLPでは、とくに導入効果が大きい。

Document Picture-in-Picture API とその他のアップデート

Document Picture-in-Picture API とその他のアップデート

CSS以外でも重要な進展があった。Firefox 151でDocument Picture-in-Picture APIがデスクトップ向けに導入され、Web Serial APIもFirefoxデスクトップとChrome Androidでサポートが拡大されている。

Document Picture-in-Picture API の概要

従来のPicture-in-Picture APIは <video> 要素を常に前面の小窓で表示する機能だった。一方、Document Picture-in-Picture APIは任意のHTMLコンテンツを含むウィンドウを常に最前面に表示できる。

これにより、ビデオ会議の参加者グリッドや株価ティッカー、タイマーといったインタラクティブなオーバーレイを、ページ遷移後も維持できるようになる。デスクトップ向けのプログレッシブウェブアプリ(PWA)でとくに威力を発揮するAPIだ。

Web Serial API のプラットフォーム拡大

Web Serial APIは、マイクロコントローラーや3Dプリンター、開発ボードといったシリアルデバイスとウェブサイトが直接通信するための仕組みだ。Firefoxでは専用のサイト権限アドオンを導入することで安全に管理できる設計になっている。

Chrome 148ではAndroid向けにもサポートが拡大され、モバイルデバイスからシリアル機器を制御するユースケースが現実的になった。IoT分野や教育用途での活用が今後広がると見られている。

この記事のポイント

  • 2026年5月のブラウザ安定版で、複数のCSS機能とHTML属性がBaseline Newly availableに到達した
  • :open 疑似クラスで開閉状態のスタイリングが一元的に書けるようになった
  • 名前のみのコンテナクエリにより、サイズ条件なしで親コンテナの存在を参照できる
  • style() クエリでカスタムプロパティを条件としたテーマ切り替えがCSSだけで実装可能
  • <video><audio>loading="lazy" でメディアの遅延読み込みがネイティブ化され、初期ロードの負荷が軽減される
CSSのletter-spacingでテキスト表示を切り替える実装テクニック

CSSのletter-spacingでテキスト表示を切り替える実装テクニック

CSSでテキストを一文字ずつ表示したり、特定の単語を切り替えたりする演出は、直感的には難しいものだ。::nth-letter()のような仮想的なセレクタがあれば楽だが、現状のCSS仕様には存在しない。しかし、letter-spacingプロパティの負の値とcolor: transparentを組み合わせることで、限定的ながらも文字単位の表示制御が実現できる。

CSS-Tricksの記事では、このテクニックを使ってチェックボックス操作によるラベル切り替えや、アクロニムの全文表示といった実装例が紹介されている。本記事ではその仕組みと実装手順を掘り下げ、日本国内のWeb制作現場での活用ポイントを考察する。

letter-spacingの基本と隠しテキストの仕組み

letter-spacingの基本と隠しテキストの仕組み

正の値と負の値の効果

letter-spacingプロパティは、各文字の右側に追加されるスペースを調整する。正の値では文字間隔が広がり、負の値ではグリフボックスの幅が縮まる。値を十分に小さく設定すると、隣り合う文字同士が重なり合い、最終的には一箇所に集約された状態になる。

この状態でテキストの色をtransparentに指定すれば、ユーザーからは完全に見えなくなる。逆に正の値に戻すと文字が再び分離して表示される。この性質をアニメーションと組み合わせることで、表示と非表示を切り替える演出が可能になる。

負のletter-spacingで重なる文字
サンプルテキスト
letter-spacing:-0.5ch の状態。文字同士が接近し、重なりが生じている
正の値に戻した読みやすい状態
サンプルテキスト
letter-spacing:0ch (デフォルト)。文字が分離し可読性が戻っている

上記のデモでは、負の値によって文字が詰まったビジュアルと、正の値(0)で通常表示に戻る様子を並べている。実際にはtransitionプロパティを加えることで、この変化をなめらかに動かせる。

ch単位を使う理由

文字の重なり具合を指定する負の値には、ch単位が特に相性が良い。1chは数字の「0」のグリフ幅に相当する相対単位であり、フォントファミリーやサイズに応じて自動調整される。これにより、使用する書体が変わっても一貫した重なり効果を維持しやすくなる。

例えばletter-spacing: -1chを指定すると、各文字が1文字分ずつ左に詰められ、理論上は完全に重なる。実際にはフォントのデザインやカーニングによって微妙なズレが生じることもあるが、調整の起点として扱いやすい。

チェックボックスと組み合わせたテキスト切り替え

チェックボックスと組み合わせたテキスト切り替え

HTMLとCSSのコード例

このテクニックを応用すると、チェックボックスの状態に応じてラベルテキストを動的に切り替えるUIを作成できる。以下は、クリックによって「入会する」のような案内文が「ようこそ」メッセージに変化するパターンだ。

input:checked + label .initial-text {
  letter-spacing: -2ch;
  text-indent: -1.5ch;
  transition: 0.4s letter-spacing cubic-bezier(.8, -.5, .2, 1.4), 
              0.1s text-indent;
}

input:checked + label .revealed-text {
  letter-spacing: 0ch;
  color: #1a1a2e;
  transition:
    0.4s letter-spacing cubic-bezier(.8, -.5, .2, 1.4) 0.3s, 
    0.8s color 0.4s;
}
非チェック状態(Before)
会員登録して特典を受け取る
登録完了しました
案内テキストが表示され、完了メッセージは letter-spacing で潰されて非表示
チェック状態(After)
会員登録して特典を受け取る
登録完了しました
チェックすると案内テキストが letter-spacing で潰れて消え、完了メッセージが表示される

デモではoverflow: clipが適用されたコンテナ内で、一方のテキストがletter-spacing: -2chtext-indentで左に押し出され、もう一方が通常の間隔に戻る仕組みだ。実際の環境ではチェックボックス操作によりこれらのプロパティが切り替わる。

アニメーションの調整ポイント

CSS-Tricksの著者Carlo Daniele氏の実装例では、cubic-bezier(.8, -.5, .2, 1.4)というイージングが使われている。この曲線は、変化の途中で値が目標値を超えて戻る「バウンス」効果を生み出し、文字が勢いよく離れるような動きを演出する。

また、2つのテキストのtransition-delayをずらすことで、古いテキストが消え始めてから新しいテキストが現れるまでの間に自然なオーバーラップが作られている。この遅延調整は、ユーザーが違和感なく情報の切り替わりを認識できるようにするための工夫だ。

アクロニムの全文表示テクニック

アクロニムの全文表示テクニック

::first-letterの活用

UNICEF(United Nations International Children’s Emergency Fund)のようなアクロニムを題材に、各単語の最初の文字だけを常に表示し、ホバー時に残りの文字を出現させるテクニックが紹介されている。

.acronym-word {
  letter-spacing: -1ch;
  color: transparent;
}

.acronym-word::first-letter {
  color: #1a1a2e;
}

figure:hover + .acronym .acronym-word {
  letter-spacing: 0ch;
  color: #1a1a2e;
  transition: letter-spacing 0.4s cubic-bezier(.8, -.5, .2, 1.4);
}
未ホバー時(Before)
United Nations International Children’s Emergency Fund
U N I C E F
各単語の頭文字(U N I C E F)だけが表示されている
ホバー時(After)
United Nations International Children’s Emergency Fund
完全な単語が展開され、アクロニムの正式名称が読める

::first-letter疑似要素で頭文字だけを黒く表示し、残りの文字はcolor: transparentで不可視にしておく。ホバーイベントでletter-spacingを0に戻すと、すべての文字が可視状態で展開される仕組みだ。

実装の注意点

このパターンでは、各単語を個別の<span>で囲む必要がある。単一のテキストブロックに対して::first-letterは最初の1文字にしか適用されないからだ。UNICEFの例のように6つの単語があれば、6つの要素でマークアップすることになる。

また、スクリーンリーダーはcolor: transparentのテキストも読み上げるため、アクセシビリティ面では注意が必要だ。このテクニックはあくまでビジュアル面の演出であり、情報の一次的な伝達手段としては適さない。重要なテキストはaria-labelなどで別途提供するか、この効果を装飾的な目的に限定するのが安全だ。

実務での活用アイデア

実務での活用アイデア

このテクニックは、以下のようなシーンで効果を発揮する。

申し込みボタンのラベル切り替え
「送信する」から「送信完了」へ、ボタン内のテキストをスムーズに変化させる。ユーザーの操作完了を直感的に伝えられる。
用語集やFAQのアクロニム展開
「SEO」にカーソルを合わせると「Search Engine Optimization」が表示される仕掛け。補足情報を必要なときだけ見せられる。
ナビゲーションの状態表示
現在位置を示すテキストを他の項目と視覚的に区別し、アクティブ項目を強調する効果として使える。

いずれも、派手なアニメーションライブラリを使わずにCSSだけで完結するのが利点だ。パフォーマンス面でもJavaScriptによるDOM操作より軽量で、メインスレッドへの負荷が少ない。

制約と対応ブラウザ

制約と対応ブラウザ

letter-spacingのアニメーションは主要なモダンブラウザで広くサポートされている。ただし、cubic-bezierによるバウンス効果は、イージングの値によっては環境間で微妙な見え方の差が出ることがある。

また、ch単位はフォントの「0」の幅に依存するため、和文フォントと欧文フォントが混在する日本語サイトでは、想定よりも文字の重なり方が異なるケースがある。実装時は実際のフォントスタックで表示確認を行うことが重要だ。

この記事のポイント

  • letter-spacingの負値とcolor: transparentを組み合わせると、文字を一箇所に重ねて不可視にできる
  • チェックボックスの状態に応じたラベル切り替えは、transition-delayの調整で自然なUI演出になる
  • アクロニムの全文表示には::first-letterと単語ごとの<span>分割が必要
  • アクセシビリティに配慮し、重要な情報は視覚効果だけに依存しない設計が求められる
CSS最新情報まとめ。Safariテスト手法、::checkmark、データ属性によるアンカー制御

CSS最新情報まとめ。Safariテスト手法、::checkmark、データ属性によるアンカー制御

Web制作の現場では、新しいCSS機能のキャッチアップとブラウザ間の動作検証が日々の課題だ。CSS-Tricksの定期連載「What’s !important」第12回では、5月末時点で注目すべき6つのトピックが取り上げられている。

実機がないSafariでのテスト手法から、スタイル付与が難しかったチェックマークを操作できる新疑似要素、さらには一度は策定が見送られたHTML属性に代わるデータ属性を用いたアンカー制御テクニックまで、幅広い知見が共有された。

本記事ではこれらのトピックを整理し、実務への応用ポイントを解説する。とくにCSS-Tricksの著者Geoff Graham氏が自ら考案したアンカー制御の代替手法は、現場の制約を乗り越えるヒントになるはずだ。

Safariがない環境でSafariテストを実施する手法

Safariがない環境でSafariテストを実施する手法

Webブラウザのシェアで2番手に位置するSafariだが、その利用はAppleデバイスに限定されている。macOSやiOSを持たない開発者にとって、Safari専用のバグ潰しやレイアウト確認は長年の悩みの種だった。

Frontend Mastersの記事でDeclan Chidlow氏が解説したのは、予算や環境に制約がある状況下での実践的なSafariテスト手法だ。物理デバイスを持たずに検証するアプローチは、大きく3つのカテゴリに分けられる。

クラウド型テストサービス
BrowserStack、LambdaTest 等のサービスを使い、クラウド上の実機Safariで検証する。クロスブラウザテストの定番手法であり、全機能が動作する。
Playwright / WebKit ビルドの活用
PlaywrightのWebKitビルドをLinuxで動かせば、Safariのレンダリングエンジンをローカル検証できる。UIの挙動や自動テストに適している。
Epiphany(GNOME Web)ブラウザ
Linuxで動作するEpiphanyブラウザはWebKitエンジンを採用している。Safariと完全に同一ではないものの、簡易的な互換性チェックに使える。
クラウド型  ローカル自動化  WebKit系ブラウザ

どの手法を選ぶべきか

最も確実なのはクラウド型のテストサービスだが、無料枠には限りがあり、動作速度もローカル環境に劣る。一方PlaywrightのWebKitビルドは、Safariのレンダリングエンジンを手軽に再現できる点で優れている。ただしフォントレンダリングや一部のOS依存機能まではカバーしきれない。

重要なのは「どのレベルで検証が必要か」の線引きだ。レイアウトの崩れやCSSプロパティの対応状況を確認するだけならPlaywrightで十分だが、タッチ操作やスクロール挙動、Apple Pay連携などの最終検証は、必ず実機かクラウドサービスで行うべきである。

::checkmark疑似要素が解決するチェックマークのスタイル課題

::checkmark疑似要素が解決するチェックマークのスタイル課題

チェックボックスやラジオボタン、セレクトボックスのチェック状態を示すマーク。このUIパーツは長年、開発者の手によって擬似的に再現されてきた。本来のチェックマーク(チェック状態を示すインジケーター)にはCSSで直接スタイルを当てられなかったからだ。

Sunkanmi Fafowora氏がPiccalilliで紹介した::checkmark疑似要素は、この制約を根本から解決する。チェックボックスだけでなく、ラジオボタンやセレクトボックスのチェック状態にも作用する点がポイントだ。

従来の手法(Before)
チェックボックス本体を非表示にし、label要素に背景画像やCSSシェイプで擬似チェックマークを描画する。コード量が多く、アクセシビリティ上の注意点も多い。
::checkmark 疑似要素(After)
ブラウザ標準のチェックマークに対して、::checkmark で色・サイズ・形状を直接スタイリングできる。セマンティックなHTMLを維持したまま見た目をカスタマイズ可能。

この機能がブラウザに実装されれば、チェックボックス周りのCSSトリックは大幅に削減されるだろう。とくにフォームのブランディング要件が厳しいプロジェクトでは、作業工数の縮小に直結する。

border-shapeとshape()で広がるシェイプ表現の選択肢

border-shapeとshape()で広がるシェイプ表現の選択肢

CSSで複雑な図形を描くとき、これまではclip-pathが主戦場だった。Temani Afif氏がCSS Tipで示したのは、border-shapeプロパティとshape()関数を組み合わせるアプローチだ。

clip-pathが要素全体を切り抜くのに対し、border-shapeは境界線に沿ってシェイプを適用する。この違いにより、輪郭のみのシェイプや、塗りつぶしと輪郭を組み合わせた表現が容易になる。

clip-path のみの表現
要素を指定したシェイプで切り抜く。切り抜かれた部分は非表示になり、背景も透過する。輪郭だけを残す表現には追加の工夫が必要。
border-shape + shape()
境界線に沿ったシェイプ変形が可能。塗りつぶし・輪郭のみ・切り抜きの3パターンを同じシェイプ定義から切り替えられる。
Afif氏のデモでは、波型シェイプをアウトライン版・塗りつぶし版・切り抜き版の3種で提示している。

実務での活用シーンとしては、カードUIの装飾枠や、セクション区切りに使うカスタムシェイプが考えられる。とくにECサイトの商品カードやブランドページのビジュアルセクションでは、微妙な形状の差別化がUIの印象を大きく左右する。

sibling-index()とsibling-count()がもたらす数理レイアウト

sibling-index()とsibling-count()がもたらす数理レイアウト

兄弟要素の中で「自分が何番目か」「兄弟全体で何個あるか」をCSSだけで取得できるsibling-index()sibling-count()。この2つの関数はBaseline(ブラウザ間の相互運用が確立された機能群)への移行が目前に迫っている。

Smashing MagazineでDurgesh Pawar氏が公開した詳細な解説では、これらの関数を使った数学的レイアウトの実例が数多く紹介されている。たとえば、兄弟要素の数に応じてグリッドの列数を動的に変えたり、要素の位置に比例したスタイルを適用したりといったパターンだ。

STEP 1 CSSが兄弟関係をカウント
STEP 2 総数や位置に応じて計算式を適用
STEP 3 レイアウトや色・サイズが動的に変化
従来はJavaScriptで要素数を取得しCSS変数に渡す必要があったが、CSS単独で完結する。

とくにCMSで生成されるリストや、ユーザー投稿型のコンテンツ一覧では、アイテム数が動的に変動する。このようなシーンで、JavaScriptに頼らずCSSだけでレイアウトを最適化できる価値は大きい。Pawar氏の記事ではView Transitionsに関する連載もCSS-Tricksで展開されており、合わせて参照することを勧める。

anchor属性の代案としてのデータ属性制御テクニック

anchor属性の代案としてのデータ属性制御テクニック

これはCSS-Tricksの著者Geoff Graham氏自身の取り組みだ。CSSアンカー位置指定において、HTML属性anchorの策定が見送られたことを受け、同氏はデータ属性とattr()関数を用いた代替手法を考案した。

アンカー位置指定とは、ある要素(ターゲット)を別の要素(アンカー)からの相対位置で配置する仕組みである。ポップオーバーやツールチップの位置決めに使われる。本来anchor属性は、このターゲットとアンカーの紐付けをHTML上で宣言的に行うために提案されていた。

見送られた anchor 属性(Before)
<div anchor="anchorA">Boat A</div> <div id="anchorA">Anchor A</div>
シンプルで直感的だが、策定プロセスでドロップされた。
Graham氏のデータ属性手法(After)
<div data-boat="anchorA">Boat A</div> <div data-anchor="anchorA">Anchor A</div>
カスタム識別子を使う場合と、attr()で直接値を取得する場合の2パターンを提示。

この手法の実用性は、CSSのattr()関数の進化に依存している。attr()<custom-ident>型をサポートするようになれば、データ属性の値をCSS内で参照し、アンカー名として利用できるようになる。ブラウザ実装の進捗を注視しつつ、先行してHTML構造をデータ属性ベースに整えておくことは、将来の移行コストを下げる有効な準備だ。

State of CSS 2026に見る開発者の学習負荷と向き合い方

State of CSS 2026に見る開発者の学習負荷と向き合い方

毎年恒例のState of CSS調査が2026年版の回答受付を開始した。今回の特徴は、冒頭文から明確に打ち出された「取捨選択」の姿勢である。調査の主催者は、CSSの進化があまりに速く、すべてを追いかけることが逆に開発者の負担になっている現状を率直に認めている。

従来の課題
新機能が次々と登場し、キャッチアップすべきリストが際限なく増える。知識の陳腐化への不安が常につきまとう。
2026年版の方向性
調査対象の機能を厳選し、本当に重要なものだけに絞り込む。学習の優先順位付けを支援する。

CSS-TricksのGraham氏もこの方針に賛同しつつ、「CSSの新機能を学んでいるときにさらに別の機能がリリースされる感覚は、圧倒的でありながら最高の体験でもある」とコメントしている。業務で必要な機能を見極め、それ以外は「面白そうだから」という理由で触れる余裕も持ちたいところだ。

ちなみに今回の調査期間中、Firefox 151がリリースされ、コンテナスタイルクエリがBaselineに到達した。デスクトップ向けではSafariの未対応が残るものの、モバイル含め多くの環境で動作する段階に入っている。またDocument Picture-in-Picture APIも新たに追加され、Webプラットフォーム全体の進化は依然として加速中だ。

この記事のポイント

  • Safariのテストにはクラウドサービス・Playwright WebKitビルド・Epiphanyブラウザの3段階がある
  • ::checkmark疑似要素は、チェックボックス・ラジオ・セレクトのチェック状態を直接スタイリングできる
  • border-shapeshape()の組み合わせで、輪郭・塗りつぶし・切り抜きを同一シェイプから切り替え可能
  • sibling-index()sibling-count()により、CSSだけで兄弟要素の位置と総数を取得できる
  • 見送られたanchor属性の代わりに、データ属性とattr()を組み合わせたアンカー制御が提案されている
AI時代のテクニカルライティング、人間が書く意味とは

AI時代のテクニカルライティング、人間が書く意味とは

テクニカルライティングの需要が大幅に減少している。CSS-Tricksの編集長Geoff Graham氏が公開した同サイトのトラフィック統計によれば、2020年から2025年にかけてアクセス数は明確な下降線を描いている。Stack Overflowの質問数減少と同様の傾向であり、業界全体に共通する構造変化だ。

スタンフォード大学の「2025 AI Index」によると、企業のAI導入率は2024年に急速に上昇した。開発者がドキュメントを読まずにIDE内のチャットで回答を得る時代において、従来型のリファレンス執筆の価値は再定義を迫られている。

しかしである。だからといって人間による技術記事が不要になったわけではない。むしろ、AIが生成する「正解」では届かない領域にこそ、書き手の存在意義がある。本記事ではCSS-Tricksの見解を踏まえつつ、AI時代のテクニカルライティングが目指すべき方向性を考察する。

テクニカルライティングはなぜ必要とされ続けるのか

テクニカルライティングはなぜ必要とされ続けるのか
AIが得意な領域
リファレンス プロパティ定義と基本コード例の提示
MDNや仕様書の情報を要約して即答する
人間が輝く領域
実体験 試行錯誤のプロセスと失敗からの学び
コードの背後にある思考と判断の文脈を共有する

AIは人間の欲望や努力なしには新しいことを学べない。ボットが動くのは与えられたプロンプトに対してだけであり、技術を前進させるのは依然として人間の動機だ。Graham氏は「AIをテクニカルライティングの新たな主要読者と見なすつもりはない」と明言している。学習意欲のある実在の人間こそが、この営みを支え続ける存在である。

仕様書と人間のあいだを埋める役割

CSS-Tricksに掲載されているCSSアルマナック(リファレンス集)は2009年から継続的に更新されてきた。一見するとAIチャットで代替可能に思える領域だが、Graham氏はこの役割を「技術的な話題をきわめて人間的な説明で伝えること」と定義する。向かいの席に座る開発者とコーヒーを飲みながら話すような距離感だ。

仕様書はブラウザ実装の正確性を担保するために意図的に緻密に書かれている。CSS-Tricksはその厳密さを崩さずに、アクセスの敷居を下げる翻訳者の役割を担ってきた。この「技術と人間のあいだを埋める」機能は、AIが要約を生成できるようになった現在でも、体験に根ざした説明という点で差別化される。

AI時代の書き手が立つべき場所

AI時代の書き手が立つべき場所
従来の執筆対象(Before)
CSSプロパティの基本構文とサンプル
ブラウザ対応表の転載
公式ドキュメントの要約
※いずれもAIが数秒で生成可能
これからの執筆対象(After)
クライアント案件で遭遇した実問題の解決録
失敗と修正を経た思考プロセスの共有
特定の制約下でのクリエイティブな回避策
※AIが真似できない「経験の質」が価値になる

Graham氏はテクニカルドキュメントを書く価値が以前より下がったと率直に認める。仕様書やMDNは充実しており、開発者の素朴な疑問はIDE内チャットで即時に解決される。しかしCSS-Tricksは2007年の開設当初から「アイデアの共有プラットフォーム」として機能してきた。ゲスト執筆者748名が蓄積してきた知見は、単なるドキュメントの代替ではない。

新たな執筆指針〜AIにできないことを書く

新たな執筆指針〜AIにできないことを書く

実体験を軸にすえる

AIはCSSプロパティの定義と簡単なコード例を提示するのが得意だ。既存の技術ドキュメントから引っ張ってくるだけなので、その領域で競うのは得策ではない。Graham氏が推すのは「クライアントから求められた未経験の要件に挑んだ話」のような、実体験に根ざした記事である。

人間は課題に直面したときにもっとも深く学ぶ。初心者から理解者に至るまでの過程そのものが、読者に使えるメンタルモデルを提供する。たとえそのモデルが最終的にAIプロンプトの作成に使われるとしても、思考の枠組みを共有する価値は揺るがない。

権威であろうとしない

CSS-Tricksは「正しいやり方」を保証するサイトではない。CSSには複数のアプローチがあり、書き手自身のメンタルモデルにもっとも馴染む方法が最善であるという立場だ。単なるアイデアの種を共有することにも価値があるとGraham氏は強調する。

「経験はあっても冷笑的になるな」というのが同サイトの指針だ。専門家であることと経験者であることは同じではない。まだ未解決の疑問があっても、試したことの報告には意味がある。

引用を惜しまない

優れた記事は先人の知恵の上に成り立つ。すべてを独自の知見として見せようとする誘惑は強いが、Graham氏は「私たちは互いの仕事の上に構築している」と明言する。ハイパーリンクによる健全な引用文化こそ、ブログの原点である。

検索エンジン最適化よりも読者最適化を

検索エンジン最適化よりも読者最適化を
SEO重視の罠
キーワードを詰め込んだ不自然な文章
クリックベイト型の誇張された見出し
検索エンジン向けに整形された構成
※検索トラフィック自体がAI回答に奪われている
人間向けの執筆
特定の読者層に刺さるトピック選定
一貫した声とトーンを保つ文体
異なる学習スタイルに配慮した説明
※かつてGoogleが評価していた本質的な品質基準

CSS-Tricksは数年前にSEOに軽く手を出したものの、本格的に注力することはなかった。キーワードの詰め込みもクリックベイト的な見出しも避け、人間のための文章、適切な構造、一貫したトーンに集中してきた。皮肉なことに、これらはかつてGoogleが重視すると表明していた要素そのものだ。

Graham氏は検索トラフィックがAI生成回答に奪われている現実を直視しつつも、CSS-TricksがAI回答の参照元として表示されるかどうかにさえ「関心があるか確信が持てない」と率直に述べる。状況は流動的であり、考え方も進化させなければならない段階だ。

AIを執筆に使うなら補助領域に限定する

AIを執筆に使うなら補助領域に限定する

Graham氏は執筆そのものへのAI利用に否定的だ。理由は二つある。第一に、AIの出力は常に正確とは限らない。第二に、AIは書き手個人の声を希釈してしまう。どちらも執筆という営みにとって致命的な欠陥である。読者がすでにIDEで得られるAI説明と変わらない内容を記事で提供する意味はない。

ただしGraham氏はAIを全面否定しているわけではない。スペルチェックやMarkdownからHTMLへの変換、公開スケジュールの管理といった「執筆とは直接関係のない低負荷な作業」には積極的に活用している。これは今日「AI」と呼ばれる機能の多くが、ブーム以前は単に「自動化」と呼ばれていたものだという冷静な視点に立っている。

この記事のポイント

  • テクニカルライティングの需要はAIの普及により構造的に減少しているが、人間による記事の価値が消えたわけではない
  • 実体験に基づく試行錯誤のプロセス共有が、AIとの差別化における最大の武器になる
  • SEOやAIOに過度に依存せず、特定の読者に向けて明確に書くという基本に立ち返るべき
  • AIはスペルチェックやフォーマット変換など執筆周辺の自動化に限定して使うのが現実的
2026年4月のBaseline新機能、contrast-color関数やsearch要素が利用可能に

2026年4月のBaseline新機能、contrast-color関数やsearch要素が利用可能に

2026年4月のBaseline月次ダイジェストが公開された。新たに利用可能になった機能として、CSSのcontrast-color()関数やJavaScriptのMath.sumPrecise()メソッドがある。合わせて、search要素やARIA属性リフレクションなど、すでに広く使える段階に達した機能も紹介されている。

今回のアップデートは、アクセシビリティ対応と開発効率の両面で重要な節目だ。ブラウザが自動的に最適な色を算出したり、セマンティックな構造をネイティブに解釈したりする機能が揃い、従来はカスタム実装に頼っていた領域が標準化されつつある。

この記事では、2026年4月のBaselineダイジェストの内容をもとに、新機能の具体的な使い方と、それが開発現場にもたらす変化を解説する。

Baselineとアクセシビリティをめぐる2026年の動向

Baselineとアクセシビリティをめぐる2026年の動向

web.devの記事では、A11y Upが公開した「Baseline and accessibility in 2026」という分析が紹介されている。この分析の核心は、アクセシビリティ対応をウェブ標準に委ねることで、開発の堅牢性と効率が大きく向上するという主張だ。

これまで多くの開発チームは、スクリーンリーダー対応やキーボードナビゲーションといったアクセシビリティ機能を、カスタムのJavaScript実装で再現してきた。しかし、そうした手作りのソリューションは往々にして壊れやすく、支援技術との相性問題を抱え、メンテナンスコストも高かった。

Baselineは、ある機能が主要ブラウザで相互運用可能になった時点を知らせる指標として機能する。この指標を活用すれば、開発者は標準機能への移行タイミングを判断しやすくなる。結果として、ブラウザが自動的に正しいセマンティクスをスクリーンリーダーに伝えてくれるため、開発者が手作業で調整する負担が減るというわけだ。

従来のアプローチ(カスタム実装依存)
開発者 手作りJSでアクセシビリティ実装 支援技術 誤動作・破損のリスク
⚠ メンテナンスコストが高く、壊れやすい
Baseline標準に則ったアプローチ
開発者 標準のsearch要素を使用 ブラウザ 自動でARIAロールを割り当て
✅ 堅牢でメンテナンスフリー

このデモが示すように、カスタム実装に頼る旧来の手法から、標準化された要素やAPIに移行することで、アクセシビリティの品質が安定し、開発者の負荷も低減する。

Baselineで新たに利用可能になった機能

Baselineで新たに利用可能になった機能

2026年4月の時点で、主要ブラウザ(Chrome、Firefox、Safari)すべてがサポートを開始し、Baseline newly available(新規利用可能)と位置づけられた機能が2つある。CSSのcontrast-color()関数と、JavaScriptのMath.sumPrecise()メソッドだ。

CSSのcontrast-color()関数

contrast-color()は、指定した背景色に対して最も読みやすい対照色(通常は黒か白)をブラウザが自動的に算出するCSS関数だ。動的なテーマエンジンやカスタマイズ可能なコンポーネントを扱う際、開発者がこれまで手作業で管理してきた「背景色に応じた文字色の切り替え」という負担を大幅に軽減する。

具体的な動作として、関数にベースとなる色を渡すと、ブラウザのエンジンがその色の輝度を評価し、最もコントラスト比が高い色を返す。これにより、ユーザーが好みの背景色を選べるUIでも、文字が読みにくくなる問題を自動的に回避できる。

.card-header {
  background-color: var(--dynamic-bg-color);
  /* 背景色に応じて自動的に最適な文字色が決まる */
  color: contrast-color(var(--dynamic-bg-color));
}
背景色 #1a1a2e(暗色)
contrast-color が自動で白文字を選択
コントラスト比 約15.3:1(WCAG AAA達成)
背景色 #fff8e1(明色)
contrast-color が自動で黒文字を選択
コントラスト比 約14.8:1(WCAG AAA達成)

上記のデモはcontrast-color()の概念を示したイメージだ。実際のブラウザでは、この関数が自動的に背景色を分析し、最も読みやすい文字色を適用する。中間的な明るさの背景色に対しては、ブラウザがどちらを選ぶか注意深く確認する必要があるが、大半のケースでは手動の分岐ロジックが不要になる。

Math.sumPrecise()メソッド

JavaScriptで浮動小数点数の合計を計算する際、従来のArray.prototype.reduce()や単純なループでは、丸め誤差が蓄積する問題があった。金融計算やテレメトリデータの集計といった、正確さが求められる場面ではこの誤差が致命的になることもある。

Math.sumPrecise()は、この問題に対処するために設計された静的メソッドだ。数値のイテラブル(配列など)を受け取り、精度を保ったまま安全に合計を返す。

// 従来の方法では浮動小数点誤差が発生する可能性がある
const values = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0];
const preciseTotal = Math.sumPrecise(values);
// 誤差なく正確な合計値を返す

内部的には、標準化された高精度な加算アルゴリズム(Kahan summation algorithmや類似の手法)を用いて、丸め誤差を最小化する。ECサイトの売上集計や、センサーデータの分析など、正確性が重視されるシナリオで特に有効だ。

従来の Array.reduce() による加算
[0.1, 0.2, 0.3].reduce((a,b)=>a+b)
結果: 0.6000000000000001
通貨計算では誤差が問題になる
Math.sumPrecise() による加算
Math.sumPrecise([0.1, 0.2, 0.3])
結果: 0.6
正確で信頼できる

この関数を使うことで、フロントエンドでの計算結果に対する信頼性が一段上がる。特に数値の正確さがビジネス上の要件に直結するアプリケーションでは、導入を検討する価値が高い。

Baselineで広く利用可能になった機能

Baselineで広く利用可能になった機能

以下の機能は、すでに主要ブラウザで長期間サポートされ、Baseline widely available(広く利用可能)のステータスに達した。実質的にどのプロジェクトでも安心して採用できる段階だ。

search要素

HTMLのsearch要素は、検索フォームやフィルタリング機能といった、サイト内の検索体験を構成する要素群を明示的にラップするためのコンテナだ。従来はdivやformタグで代用されていたが、search要素を使うことでアクセシビリティ上の利点が生まれる。

具体的には、ブラウザがsearch要素に対して暗黙的にARIAランドマークロール「search」を割り当てる。これにより、form要素にrole=”search”を手動で付与する必要がなくなる。スクリーンリーダーのユーザーは、このランドマークを頼りに検索インターフェースへ素早く移動できる。

<search>
  <form action="/site-search">
    <label for="query">ドキュメントを検索</label>
    <input type="search" id="query" name="q">
    <button>実行</button>
  </form>
</search>
div 要素で検索エリアをマークアップ(非推奨)
<div>
<form role=”search”>…</form>
</div>
role属性の記述が必要。忘れるとアクセシビリティが損なわれる
search 要素でマークアップ(推奨)
<search>
<form>…</form>
</search>
ブラウザが暗黙的に role=”search” を付与。記述の手間と漏れがない

このシンプルな変更だけで、検索機能のアクセシビリティがワンランク向上する。既存のプロジェクトでも、該当するセクションをsearch要素に置き換えるリファクタリングを検討するとよい。

Web Authenticationの公開鍵アクセス

パスワードレス認証を実現するWeb Authentication(WebAuthn)APIにおいて、公開鍵情報の取り扱いが大幅に簡素化された。AuthenticatorAttestationResponseインターフェースに追加されたgetPublicKey()やgetPublicKeyAlgorithm()といったメソッドを使うことで、開発者が生のバイナリデータを手動で解析する必要がなくなった。

これまで公開鍵を抽出するには、CBOR(Concise Binary Object Representation)やDERエンコーディングといったバイナリ形式を手作業でパースする処理が必要だった。公開鍵の取り出しに失敗したり、アルゴリズムを誤認したりするリスクが常につきまとっていた。新しいメソッドはブラウザが直接プロパティとして公開鍵情報を提供するため、そのような低レイヤの処理が一切不要になる。

パスキー(Passkeys)の普及が加速する中、このAPIの安定化は認証フロー全体の信頼性を一段引き上げる要素だ。

String.prototype.isWellFormed()とtoWellFormed()

JavaScriptの文字列は内部的にUTF-16でエンコードされている。複雑な文字や絵文字の中には、サロゲートペアと呼ばれる2つの16ビットコード単位で表現されるものがある。文字列を途中で切断してしまうと、ペアの片方だけが残った「孤立サロゲート」という不正な文字が生まれる。

isWellFormed()は、文字列に孤立サロゲートが含まれていないかを真偽値で返すメソッドだ。toWellFormed()は、もし不正なサロゲートが見つかった場合、それをUnicodeの置換文字(U+FFFD)に置き換えた新しい文字列を返す。encodeURI()など、不正な文字列が渡されるとURIErrorをスローする関数にデータを渡す前に、これらのメソッドで検証と修正を行うのが主な用途だ。

const rawString = getUserInput();
// 不正な文字が混入していないか確認
if (!rawString.isWellFormed()) {
  // 問題があれば安全な形に修正してから処理を続行
  const cleanString = rawString.toWellFormed();
  const encoded = encodeURI(cleanString);
  // 安全にAPIリクエストなどを実行
}
不正な文字列の処理(従来)
不正文字列 encodeURI() URIError発生
アプリケーションがクラッシュするリスク
isWellFormed / toWellFormed で安全に処理
不正文字列 toWellFormed() 安全な文字列 encodeURI()
例外なく処理が完了

ユーザー入力や外部APIからのレスポンスを扱う場面では、予期せぬデータ不整合による例外発生を未然に防ぐ手段として、これらのメソッドが役立つ。

ARIA属性リフレクション

これまで、ARIA属性の値を更新するにはelement.setAttribute(‘aria-expanded’, ‘true’)のように、DOM属性を文字列で操作する必要があった。ARIA属性リフレクションは、この手順をオブジェクトプロパティへの代入に簡略化する。

ElementインターフェースがariaExpanded、ariaChecked、ariaHiddenといったプロパティを直接公開することで、ドット記法による読み書きが可能になった。これは単なるシンタックスシュガーではなく、UIフレームワークや状態管理ライブラリがアクセシビリティ状態をより正確に追跡し、スクリーンリーダーとの同期を保つうえで重要な基盤となる。

// トグルボタンのアクセシビリティ状態を簡潔に更新
toggleButton.ariaExpanded = toggleButton.ariaExpanded === "true" ? "false" : "true";
従来のsetAttributeによる操作(煩雑)
element.setAttribute(‘aria-expanded’, ‘true’)
element.getAttribute(‘aria-expanded’)
文字列操作のため、タイポや値の形式ミスのリスクがある
リフレクションによる操作(簡潔)
element.ariaExpanded = “true”
element.ariaExpanded
プロパティとして直感的にアクセス可能

ReactやVueのようなフレームワークで状態とARIA属性を紐付ける際、従来の文字列ベースの操作に比べてコードの見通しが格段に良くなる。特に複雑なUIコンポーネントを構築するチームにとって、採用するメリットは大きい。

この記事のポイント

  • contrast-color()関数で、背景色に応じた文字色の自動選択が可能になった
  • Math.sumPrecise()で浮動小数点数の正確な合計計算を実現
  • search要素が広く利用可能になり、アクセシビリティ対応が容易に
  • WebAuthnの公開鍵抽出がメソッド一発で完了するように簡略化
  • ARIA属性リフレクションで、状態管理と支援技術の同期が強化
View Transitions、大量要素スケーリングにview-transition-classが効く

View Transitions、大量要素スケーリングにview-transition-classが効く

クロスドキュメントビュートランジション(View Transitions)は、ページ間の遷移をアプリのように滑らかにする強力なAPIだ。しかし本番環境で数十、数百の要素を扱おうとすると、途端にスケーリングの問題に直面する。1つのヒーロー画像を動かすデモは簡単だが、48枚の商品カードを個別に遷移させるとなると話が違う。

本記事では、view-transition-classと動的な名前付けの手法を用いて、大量要素を効率よく扱う方法を解説する。CSS-Tricksで公開された連載Part 2の内容を基に、実践的なパターンとアクセシビリティへの配慮までカバーする。

view-transition-classがスケーリングの鍵

view-transition-classがスケーリングの鍵

多くのチュートリアルでは、1つの要素にview-transition-name: heroを付与し、ページ間でマッチさせる。しかし実際のプロダクトグリッドでは、48枚のカードには48の一意な名前が必要になる。CSSでこれに対応しようとすると、次のような悪夢が待っている。

::view-transition-group(card-1),
::view-transition-group(card-2),
::view-transition-group(card-3),
::view-transition-group(card-4),
::view-transition-group(card-5),
::view-transition-group(card-6),
::view-transition-group(card-7),
::view-transition-group(card-8)
/* ... さらに92個続く */ {
  animation-duration: 0.35s;
  animation-timing-function: ease-out;
}

この方法は、要素数が増えるほど管理不能になる。100個の商品があれば100個のセレクタを書かなければならず、保守は事実上不可能だ。

名前とクラスの決定的な違い

ここで重要になるのが、view-transition-nameview-transition-classの使い分けだ。両者は似ているようで役割がまったく異なる。

  • nameは「同一性」を表す。ページAのサムネールとページBのヒーロー画像が「同じもの」だとブラウザに伝える。nameはページ内で一意でなければならない。重複するとトランジションは破棄される。
  • classは「スタイルのフック」だ。50の要素がview-transition-class: cardを持てば、1つのCSSルールでそれらすべてのアニメーションを制御できる。通常のCSSクラスと同じ考え方で、特定の要素を指すものではなく「こう見せたい」をグループ化する。

データベースにたとえるなら、nameが主キー、classがカテゴリ列に相当する。主キーは一意に1行を特定し、カテゴリ列はまとめてクエリをかけるために使う。

クラスを使った共通スタイルの記述

実際のCSSはこうなる。6枚のカードに6つのユニークなnameを与えつつ、アニメーションのルールはたった3つで済む。

::view-transition-group(*.card) {
  animation-duration: 0.35s;
  animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

::view-transition-old(*.card),
::view-transition-new(*.card) {
  object-fit: cover;
}

セレクタの*.cardは「view-transition-classがcardであるすべてのビュートランジショングループ」を意味する。アスタリスクはnameのワイルドカードで、classにマッチする。これでカードが60枚でも600枚でもCSSは変わらない。

従来手法(各カードに個別セレクタ)
48個のカードそれぞれに固有のルールを記述する必要がある
::view-transition-group(card-1), ::view-transition-group(card-2), …
※ 100個を超えると事実上管理不能
view-transition-class を使った改善後
共通クラスに1つのルールを書くだけ
::view-transition-group(*.card) { animation-duration: 0.35s; }
※ 何枚でもCSSは変わらない
■ 要素数に依存せず、わずか数行で全カードのアニメーションを制御できる

このように、view-transition-classは大量要素のビュートランジションにおける、スケーリングの本質的な解決策だ。CSSのみで記述する理想形であるident("card-" sibling-index())のような自動生成はまだブラウザに実装されていないが、クラスを使えば十分なスケールを得られる。

動的名前付けでパフォーマンスを最適化

動的名前付けでパフォーマンスを最適化

view-transition-classでスタイルのスケーリングは解決した。しかし、nameをページロード時にすべて付与してしまうと、別の問題が発生する。ユーザーが1枚のカードをクリックするだけでも、ページ上の全カード(48枚)のスナップショットが撮られ、疑似要素ツリーが構築されてしまうのだ。これは余計なコストであり、特にミドルレンジのモバイル端末ではトランジションのカクつきやスキップを引き起こす。

pageswapとpagerevealのライフサイクル

正しいアプローチは、nameを「ジャストインタイム」で付与することだ。ユーザーが操作したその瞬間にだけnameを設定し、遷移が終われば削除する。これにより、実際に遷移する要素だけがキャプチャされ、無駄な処理が発生しない。

流れはこうだ。

  • ユーザーが一覧ページでカードをクリックする。
  • ブラウザがナビゲーションを開始し、旧ページでpageswapイベントが発火する。
  • pageswapハンドラがクリックされたカードを特定し、view-transition-name: product-42を動的にセットする。
  • ブラウザがその要素のスナップショットを撮る。
  • 新ページが読み込まれ、pagerevealイベントが発火する。
  • pagerevealハンドラがURLからIDを読み取り、ヒーロー要素に一致するnameを割り当てる。
  • ブラウザが新旧のスナップショットをマッチさせ、モーフィングアニメーションを再生。
  • トランジションが完了したら、viewTransition.finishedのPromise解決後にnameをクリアする。

この一連の流れで、名付けられるのはたった1つの要素だけだ。48枚のカードのうち47枚は何も関与せず、無駄なスナップショットはゼロになる。

商品一覧ページ(クリック前)
カード42 data-id=”product-42″(name未設定)
ページロード時は全カードが匿名
↓ クリック
pageswap イベント発火
JavaScriptが view-transition-name: product-42 を動的に付与
↓ スナップショット → 遷移
商品詳細ページ
ヒーロー画像 pagereveal で view-transition-name: product-42 を設定
名前が一致し、モーフィング開始
■ クリックされた1要素だけがトランジションに参加し、残りは無視される

このパターンは、Astroのtransition:nameディレクティブやNuxtのビュートランジションサポートが内部的に行っていることと本質的に同じだ。フレームワークが抽象化している処理を、pageswappagerevealで直接制御していると考えればよい。

名前のクリーンアップが重要な理由

トランジション完了後にnameを削除するステップは、単なるお片付けではない。もしユーザーが一覧ページに戻り、別のカードをクリックした場合、古いnameが残っていると重複によるエラー(トランジションが即時破棄される)か、誤った要素とのマッチングが起きる。必ずviewTransition.finishedの解決後にnameをクリアすること。

実践的なパターン集

実践的なパターン集

商品グリッド以外にも、いくつかの典型的なパターンが存在する。実際のサイトで遭遇する状況に合わせて応用できる。

アスペクト比混合のフォトギャラリー

サムネールと拡大画像でアスペクト比が異なるギャラリーは、object-fit: coverで歪みを防ぎつつ、クラスで統一的に制御する。ポイントは、view-transition-name<img>自身に付与し、カードの枠やキャプションを含めないことだ。画像だけをモーフィングさせ、背景や枠は別のトランジションとして扱う。

::view-transition-group(*.gallery-item) {
  animation-duration: 0.5s;
  animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
}

::view-transition-old(*.gallery-item),
::view-transition-new(*.gallery-item) {
  object-fit: cover;
  overflow: hidden;
}

/* ライトボックス背景は別クラスでフェード */
::view-transition-group(*.lightbox-bg) {
  animation-duration: 0.3s;
}

ライトボックスの暗いオーバーレイには、別のnameとclassを与え、独立したフェードインアニメーションを適用する。画像のモーフィングと背景のフェードが並行して走り、洗練された印象になる。

タブやセクションの切り替え

ダッシュボードタブやマルチステップフォームなど、同一ページ内でのセクション遷移にも同じ手法が使える。固定ヘッダーにはanimation-duration: 0sを指定して「動かない」ようにし、コンテンツだけがスライドする感覚を出す。

::view-transition-group(*.persistent) {
  animation-duration: 0s; /* 動かさない */
}

::view-transition-group(*.tab-content) {
  animation-duration: 0.25s;
}

::view-transition-old(*.tab-content) {
  animation: slide-out-left 0.25s ease-in;
}

::view-transition-new(*.tab-content) {
  animation: slide-in-right 0.25s ease-out;
}

永続的な要素にアニメーションをかけないことで、UI全体に安定感が生まれる。

無限スクロールと動的コンテンツ

無限スクロールで後からDOMに追加された要素にも、特別な対応は不要だ。pageswapハンドラはナビゲーション発生時にDOMをクエリする。要素がその時点で存在していれば、問題なくnameを割り当てられる。唯一注意すべきは、data-idなどマッチングに使う識別子が動的に追加されたバッチ間でも一意であることだ。APIが返すIDを利用していれば問題ない。

アクセシビリティとprefers-reduced-motion

アクセシビリティとprefers-reduced-motion

アニメーションは、前庭障害を持つユーザーに吐き気やめまい、片頭痛を引き起こす可能性がある。prefers-reduced-motionメディアクエリは、OSレベルで「動きを減らしてほしい」と設定しているユーザーを検出する。ビュートランジションを導入するなら、この対応は必須だ。

@view-transition {
  navigation: auto;
}

/* アニメーションのカスタマイズはすべてこのメディアクエリ内に */
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-group(*.card) {
    animation-duration: 0.35s;
    animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  }

  ::view-transition-old(*.card),
  ::view-transition-new(*.card) {
    object-fit: cover;
  }

  ::view-transition-old(root) {
    animation: fade-out 0.2s ease-in;
  }

  ::view-transition-new(root) {
    animation: fade-in 0.3s ease-out;
  }
}

/* 動きを減らす設定の場合は0秒で即座に切り替え */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation-duration: 0s !important;
  }
}

根本的に安全を取るなら、@view-transitionの宣言自体をprefers-reduced-motion: no-preferenceで囲み、トランジションを完全に無効化する方法もある。どちらを選ぶにせよ、アニメーションを無配慮に提供することだけは避けなければならない。

prefers-reduced-motion: no-preference(通常)
カードA → 0.35sのスケール&移動アニメーション
スムーズなモーフィングが再生される
prefers-reduced-motion: reduce(設定ユーザー)
カードA → 0sで即座に切り替え
視覚的な負荷を回避し、安全に遷移する
■ 設定に応じてアニメーションの有無を切り替えるのが基本

なお、prefers-reduced-motion: reduceのユーザー向けに、完全に0秒にする代わりに短いクロスフェード(0.15秒)を提供する手法もある。動きそのものをゼロにするのが最も安全だが、穏やかなフェードなら許容できるユーザーもいる。ただし、実際にその設定に依存するユーザーでテストするまでは、0秒を選択しておく方が無難だ。

プログレッシブエンハンスメントとブラウザ対応

プログレッシブエンハンスメントとブラウザ対応

ビュートランジションは、プログレッシブエンハンスメントの理想的な例だ。ブラウザが@view-transitionルールを理解しなければ、単に無視され、通常のページ遷移が行われる。何も壊れない。エラーもレイアウトシフトも発生しない。Firefoxがまだサポートしていなくても問題はなく、Safari 18.2以降やChrome、Edgeではフル機能が使える。

唯一、@supports (view-transition-name: none)でガードする価値があるのは、トランジション専用のスタイル(スナップショット品質向上のためのcontain: paintなど)を適用する場合だけだ。それ以外は、古いブラウザでも何もせずにそのまま動く。

この記事のポイント

  • view-transition-nameは一意の識別子、view-transition-classはスタイルをグループ化するフック。クラスを使えば、数百要素でも数行のCSSでアニメーションを統制できる。
  • nameはページロード時に全要素に付与せず、pageswapとpagerevealを使ってクリック時に動的に設定する。これでパフォーマンスが大幅に向上する。
  • トランジション完了後は必ずnameをクリアし、古い名前の衝突を防ぐ。
  • prefers-reduced-motionの対応は必須。すべてのアニメーションカスタマイズをメディアクエリ内に閉じ込め、設定ユーザーには0秒または短いフェードを提供する。
  • ビュートランジションはプログレッシブエンハンスメント。未対応ブラウザでは何も起こらず、通常のページ遷移となる。
Stack Overflow質問数が激減、AI時代に問いをやめた開発者の未来

Stack Overflow質問数が激減、AI時代に問いをやめた開発者の未来

2026年5月現在、Stack Overflowの月間質問数は3,000件を下回る水準にまで落ち込んでいる。2014年のピーク時には月間20万件を超えていたことを考えると、この10年余りで実に98%以上が消失した計算だ。

CSS-Tricksに掲載された分析記事は、この急落が単にAIの台頭だけでは説明できないと指摘する。コミュニティのモデレーション方針や初心者への閉鎖性が、ChatGPT登場以前からすでに質問数の減少を招いていたという。本記事では同記事の考察を軸に、AI時代における開発者の「問う力」の行方を掘り下げる。

重要な問いはこうだ。開発者が質問をやめた世界で、AIの学習データはどう更新されるのか。次世代のコード職人は育つのか。これらの懸念はCSS-Tricksの記事全体を貫く核心でもある。

Stack Overflow質問数の急落が示すもの

Stack Overflow質問数の急落が示すもの

Stack Overflowは2008年の設立以来、開発者にとって最大級のQ&Aプラットフォームとして機能してきた。しかしData Stack Exchangeで公開されている統計は、驚くべき下落曲線を描いている。

2014年には月間20万件以上の新規質問が投稿されていた。ところが2026年には月間3,000件にも満たない状況だ。このグラフは、単なるプラットフォームの衰退を超えて、ソフトウェア開発における知識共有の在り方そのものが変質したことを物語る。

2014年のピーク時(Before)
200,000 件/月
新規質問が活発に投稿され、回答も即時についていた
初心者 熟練者 誰でも質問
2026年の現状(After)
3,000 件/月未満
98%以上の質問が消失、回答の蓄積も鈍化
AI回答 ナレッジ停滞

このグラフが示す事実は重い。Stack Overflowはソフトウェア開発の集合知として15年以上にわたり機能してきたが、その流入がほぼ止まったに等しい。CSS-Tricksの記事では、この減少を「大量のレンガが降ってくるような衝撃」と表現している。

減少の原因はAIだけではない

減少の原因はAIだけではない

ChatGPTが公開されたのは2022年11月だ。しかしStack Overflowの質問数減少は、それよりずっと前の2014年から始まっていた。CSS-Tricksの記事は、AIを「最後のとどめ」と位置づけつつ、真の要因は別にあると分析する。

厳格化するモデレーションと閉じたコミュニティ

2014年以降、Stack Overflowは質問の品質を保つためにクローズ・削除の基準を厳格化した。重複質問は容赦なく閉じられ、「すぐに回答できない質問」も排除される方針が取られた。同サイト自身が「社交的ではないが、驚くほどうまくスケールする」と述べていたほどだ。

この運用はGoogle検索経由で既存の回答に誘導するモデルとしては合理的だった。しかし初めて質問しようとする初心者にとっては、門前払いの壁にしか見えなかった。CSS-Tricksの記事は「学びたいという意欲に対して罰を与えられるようなものだ」と表現している。モデレーションの厳しさがコミュニティの新規参加を阻み、質問数の漸減を招いたのだ。

従来のStack Overflow質問フロー(Before)
質問投稿 重複チェックでクローズ ダウンボート
※ 初心者は萎縮し、質問そのものを諦めるケースが多かった
現在のAI活用フロー(After)
質問を入力 LLMが即座に回答 判断なし・即時
※ ただし回答の正確性・セキュリティリスクは未検証のまま

変化の流れははっきりしている。質問を歓迎しないコミュニティの空気がまず参加者を減らし、そこに24時間即答してくれるAIが登場したことで、残っていた質問需要も完全に吸収された形だ。

AIは問題解決の代替になるか

AIは問題解決の代替になるか

AIはコードを書ける。だが「問題を解決できる」かは別の問いだ。CSS-Tricksの記事は複数の研究を引用しながら、この点を丁寧に解きほぐしている。

AI生成コードの品質

DeepMindのAlphaCodeは競技プログラミングで人間レベルの成績を収めた。しかし実務のソフトウェア開発は競技とは異なる。コーネル大学の研究によれば、AI生成コードは「一般に単純で反復的であり、未使用の構造やハードコードされたデバッグ処理を含みやすい」という。一方で人間のコードは「構造的複雑性が高く、保守性の問題が集中する傾向がある」と報告されている。

セキュリティ面ではさらに深刻だ。VeraCodeが100のAIモデルを対象に脆弱性テストを実施したところ、AI生成コードの45%にセキュリティ上の欠陥が見つかった。CSS-Tricksの記事は「十分な検証なしにAIコードをコピー&ペーストするだけであれば、深刻なバグや脆弱性に必ず直面する」と警告する。

AI生成コードの特徴(Bad)
単純かつ反復的な構造
未使用の変数・関数が残る
ハードコードされたデバッグ処理
45%にセキュリティ脆弱性
出典: Cornell大調査 / VeraCode
人間のコードの特徴(Good)
構造的複雑性が高い
コンテキストを考慮した設計
テスト・エッジケースの考慮
保守性の問題は多いが、意図は明確
出典: Cornell大調査

MITの研究も、AIは「良いコードを書けるが、ソフトウェアエンジニアのように思考し判断することはできない」と結論づけている。GitHubが2024年8月に公開した調査では、開発者の97%以上が仕事またはプライベートでAIツールを利用しているという。AIは遍在しているが、それを使いこなす職人技は依然として人間の側にある。

生産性とモチベーションのトレードオフ

Harvard Business Reviewの研究によれば、生成AIは問題解決の生産性を高める一方で、作業者のモチベーションを低下させる副作用がある。CSS-Tricksの記事はこの点を「AIは問題解決を支援する道具としては有効だが、創造性と問題解決アプローチを代替することはできない」とまとめている。

職人はすべての道具を使いこなす。AIもその一つにすぎない。道具の有効性は、それを作った職人の技量と、それを使う工夫によって決まる。CSS-Tricksの記事が引用するCraig D. Lounsbroughの言葉が端的に示す通りだ。

AIを賢く使うための自問

AIを賢く使うための自問

CSS-Tricksの記事では、著者自身が開発作業でAIを使う際に実践している4つのチェック項目が紹介されている。このリストは、AIへの過剰依存を避けつつ生産性を高める実践知として参考になる。

STEP 1 小さく具体的な質問に分割しているか
システム全体をまとめて尋ねるのではなく、各ステップを個別に検証できる粒度にする
STEP 2 出力内容を評価し、理解しているか
生成されたコードを将来にわたって保守・修正できるか自問する
STEP 3 参照元・情報源を確認しているか
回答の根拠が架空の文献でないか、信頼できる最新手法かを確かめる
STEP 4 エッジケースまでテストしたか
ユーザーが実際にどう使うかを理解するのは人間の役割。AIには難しい領域

この4つの自問は、AIにすべてを任せるのではなく、開発者自身が主体的にコードの品質と安全性に責任を持つためのガイドラインだ。CSS-Tricksの記事は「AIにすべてを委ねるのは大きな間違いだ」と明言している。

問いをやめた先にあるもの

問いをやめた先にあるもの

記事の後半で提起される最も本質的な問いはこれだ。開発者が質問することをやめた世界で、AIの学習データはどう更新されるのか。

CSSを例に取れば、ここ数年でネスト、ビュートランジション、コンテナクエリといった仕様が急速に進化した。数年前のコードと現在のコードでは書き方が根本的に異なる。もし新たな質問と回答の蓄積が止まれば、LLMは古いプラクティスに基づいたコードを出力し続けることになる。CSS-Tricksの記事は「私たちが質問をやめ、回答をやめれば、LLMは時代遅れになるのではないか」という懸念を示している。

質問が生まれ続ける世界(Before)
開発者の質問 回答の蓄積 ナレッジ更新 AIの学習データ
※ 技術の進化に合わせて新しい質問と回答が継続的に生まれる健全なループ
質問が止まった世界(After)
質問の消失 回答の枯渇 ナレッジの停滞 LLMの陳腐化
※ CSSネストやコンテナクエリなど新技術に対応できないLLMが増えるリスク

Stack Overflowの共同創業者Jeff Atwoodはかつて「Stack Overflowはあなた自身だ」と述べた。同僚プログラマーを信頼することがプラットフォームの核心だった。CSS-Tricksの記事は読者に問いかける。「LLMも同じことをしてくれるだろうか」と。

人間はこれまでも新しい道具とのバランスを見つけてきた。AIも例外ではないだろう。しかし、問うことをやめたコミュニティからは、新しい知見も、次世代の職人も生まれにくい。その危惧がこの記事の底流にある。

この記事のポイント

  • Stack Overflowの月間質問数は2014年の20万件超から2026年には3,000件未満へと98%以上減少した
  • 減少の原因はAIだけではなく、2014年以降の厳格なモデレーションと初心者排除のコミュニティ構造が先行要因として存在する
  • AI生成コードの45%にセキュリティ脆弱性があり、コピー&ペーストだけでは深刻なリスクを招く
  • 開発者は小さな質問への分割、出力評価、参照元確認、テストの4ステップでAIと向き合うべきである
  • 質問と回答の蓄積が止まれば、LLMは新技術に対応できず陳腐化するという構造的リスクがある