feat(render): prefers-reduced-motion のエミュレーションをプロジェクト単位で追加する - #21
Open
sousuke0422 wants to merge 3 commits into
Open
feat(render): prefers-reduced-motion のエミュレーションをプロジェクト単位で追加する#21sousuke0422 wants to merge 3 commits into
sousuke0422 wants to merge 3 commits into
Conversation
…lementing it 新しい層は既定で fail-open として生まれる——実装より先に、この層自身が 失敗したときどう倒れるかを三欄(検知できるか/どう倒れるか/検知できぬ ならなぜか)で数え、層ごとの失敗経路・手当て・分類の三表へ行を足す。 対象は ①CDP エラー ②無応答 ③成功したがメディアクエリ未変化 ④matchMedia 差し替え ⑤有効な project なのに呼び出し漏れ。 Assisted-by: multi-agent-shogun-aki-tweak
…osed プロジェクト単位の bool(既定 OFF)で、storybook モードの撮影を prefers-reduced-motion: reduce のエミュレーション下で行う。 - 適用はナビゲーション前に Emulation.setEmulatedMedia を一度。撮影直前に 設定すると、初期化時に一度だけ matchMedia を読む実装(この層の主対象で ある rAF / canvas 実装の最頻形)に見えないため、OS で reduce を設定した 実利用者と同じ条件で最初から描画させる - fail-closed: 有効な project では撮影直前に REDUCED_MOTION_PROBE が CSS カスケード(constructed stylesheet の @media プローブ)と matchMedia の両輪で適用を実測し、ok === true と確かめられた場合にだけ 撮影へ進む。reduce を返さないモック matchMedia(polyfill・テスト ダブルの事故)はここで落ちる。CDP エラーは new_page と同じ環境分類、 無応答は chromiumoxide の request timeout、検証 evaluate のエラーは READY probe と同じ deadline リトライ→story 分類の Timeout - 呼び出し漏れ経路は実行時に検知できないため構造で塞ぐ: 配線は render_options_for_project の 1 箇所+単体テスト、経路全体は 「ON で絵が変わる」positive control テストが貫通して固定 - 既定 OFF の理由(有効化で baseline が一度入れ替わる)と効く範囲の 狭さ(行儀の良い rAF / canvas 実装との交差にだけ効く)を README へ - Page.setBypassCSP は使わない(本番と同じ条件で撮る) Assisted-by: multi-agent-shogun-aki-tweak
Assisted-by: multi-agent-shogun-aki-tweak
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
storybook モードの撮影で
prefers-reduced-motion: reduceをエミュレートする設定を、プロジェクト単位で追加する。既定は無効。
背景
PR #19 で入れた静止機構は
getAnimations()に載るものを止める。CSS アニメーション、トランジション、Web Animations API がこれにあたる。
実装がメディアクエリを見ているかどうかに依存しないのが、この機構の強みになっている。
一方で
getAnimations()に載らないものには原理的に届かない。requestAnimationFrameで毎フレーム描き直すものや、canvas の中の描画がこれにあたる。次に何が描かれるかは実行するまで決まらないため、終端も初期状態も定義できない。
prefers-reduced-motionのエミュレーションは、この範囲に届きうる唯一の手段になる。メディアクエリを見て動きを止める実装であれば、rAF のループ自体が回らなくなるため。
効く範囲が狭いことについて
この機構は、メディアクエリを尊重する実装にしか効かない。
無視する実装には何も起きない。
ただしそちらは PR #19 の静止機構が既に止めているため、二つは補完の関係にある。
アニメーション GIF、APNG、
<video>、SVG の SMIL には効かない。ブラウザ自身が動かすものであり、実装の行儀という概念が当てはまらないため。
したがってこの設定を入れても、flaky な差分が広く消えるわけではない。
効くのは rAF や canvas を使い、かつメディアクエリを尊重する実装という交差だけになる。
README にもこの範囲を明記した。
実装
プロジェクトに
emulate_reduced_motion列を追加した。既定は false。
有効にすると撮影される絵が変わるため、そのプロジェクトの baseline は一度入れ替わることになる。
エミュレーションはナビゲーションの前に適用している。
撮影の直前ではない。
初期化時に一度だけ
matchMediaを読む実装が、この層の主な対象になるため。ナビゲーション後に設定しても、そうした実装には何も効かない。
適用されたかどうかは、撮影の直前に実測している。
constructed stylesheet の
@media (prefers-reduced-motion: reduce)が効いたかを見るプローブと、matchMedia().matchesの二つを両方確認する。どちらかが不成立なら撮影せず失敗させる。
matchMediaが polyfill やテストダブルで差し替わっていて reduce を返さない場合も、ここで落ちる。検知できない経路について
二つある。
どちらも実装の前に洗い出し、
browser.rsのモジュール冒頭にある失敗経路の表へ記録した。ページが両方の観測を偽装した場合は検知できない。
検証はページの JS realm で走り、CDP にはエミュレート中のメディア状態を読み戻す API が存在しないため。
このリポジトリの脅威モデルは一貫して事故であり悪意ではない。
壊れた polyfill やテストダブルは捕まえるが、意図的な偽装は対象外になる。
設定が有効なのに呼び出し自体が漏れる経路も、実行時には検知できない。
呼ばれなかったことを観測する層は、それ自身も呼ばれないため。
これは構造で塞いだ。
適用箇所を
render_storyの一箇所だけに置き、プロジェクト列からの配線を単体テストで固定し、経路全体を有効時に絵が変わることを見る試験で貫通させている。検証
有効にして同じストーリーを二回撮り、PNG のバイト列が一致することを確認した。
無効と有効で絵が変わることも確認している。
描画時に一度だけ
matchMediaを読む fixture を用意し、無効なら黄、有効なら緑になるようにした。これが変わらなければ、エミュレーションが効いていないのか fixture が行儀良くないのかを区別できないため。
matchMediaを常に false を返すモックへ差し替えた fixture でも試験した。CSS 側は成立するが
matchMedia側が不成立になり、撮影せずに失敗する。変更されていないもの
Page.setBypassCSPは使っていない。script-srcまで外れて inline script が動き、DOM が変わるため。撮る絵を変えないことがこの機構の前提になっている。
PR #19 の静止機構には手を入れていない。
レビューしてほしい点
適用のタイミングをナビゲーション前にした判断が妥当か。
撮影直前の設定では、初期化時に一度だけメディアクエリを読む実装に効かない。
一方でナビゲーション前に置くと、適用から撮影までの間が長くなる。
失敗経路の表に漏れがないか。
特に「検知できない」と書いた二つについて、実際には検知しうる方法があるなら指摘してほしい。
Assisted-by: multi-agent-shogun-aki-tweak