JavaScriptのパララックスがいつも少しおかしく見える理由があります — ちょっとずれていて、レイヤーがページの半フレーム遅れで泳いでいるような感じ。雑なコードではありません。スクロールイベントがブラウザがすでに描画した後にJavaScriptに届くからです。ブラウザはこれをかつて解決しました — 粘着的なヘッダーをposition: stickyに変えた時に。Scroll-driven animationsは同じ手法を、すべてのスクロールに連動したエフェクトに一度に適用したものです。
10年間、サムネイルをヒーロー画像にアニメーションさせるには一つの技法しかなかった:前後の位置を測定し、transformでその差を偽り、再生する。FLIP。すべてのアニメーションライブラリがそれを行い、すべてが最悪のタイミングでメインスレッドのJavaScriptという代償を払ってきた。View Transitions APIはそのトリック全体をブラウザに移し、React 19.2とNext.js 16以降、契約全体はnameという一つのpropだけになった。
マイクロインタラクションの記事では、transformとopacityがコンポジタースレッド上でアニメーションするので、重いページでもボタンのアニメーションがカクつくことはないはずだと書いた。それは今も真実だ——ただし、あの記事が触れていなかった一つの留保がある。メインスレッドが長いJSタスクによってブロックされていれば、アニメーションはそもそも起動する機会すら得られないかもしれない。クリックのハンドラ自体がキューで待たされているからだ。INPはまさにこのギャップを測る指標であり、scheduler.yield()はそれを実際に埋めるための数少ない手段の一つである。
「いいね」ボタンは即座に反応し、ハートが塗りつぶされ、カウンターが増える——サーバーがまだ何一つ応答していないうちに。これはマイクロインタラクションの記事で扱った速さの錯覚よりも一歩踏み込んだものです。インターフェースがまだ存在しない状態を表示し、それがすぐに実現すると仮定しているのです。この仮定が外れたときに本当に嘘を撤回できる、安全な作り方を確認します。そして、手動の`setState`と`catch`が、`useOptimistic`が提供するものの劣化版でしかない理由も見ていきます。
CSS Houdiniは単一の技術ではなく、成熟度がまちまちな仕様の集合体です。今どの部分が本番投入に耐えるのか、`@property`とpaint workletが実際にどう動くのか、そして残りがなぜ今なお実験段階のままなのかを確認しましょう。
同じ商品、同じサーバー応答時間のふたつのストア。それでも一方は明らかに速く、信頼できるように感じられます。その差を生むのがマイクロインタラクション――ユーザーが画面上の出来事を信じられるかどうかを決める、わずか数百ミリ秒のアニメーションです。トリガー/フィードバックのモデル、ブラウザのレンダリングエンジン内部で実際に起きていること、そしてprefers-reduced-motionまで、基本原理から解説します。