test(storybook): ストーリーを追加し a11y チェックをゲート化する - #36
Conversation
ストーリーが 14 コンポーネント中 4 つにしか無く、addon-a11y も test: "todo" で違反を表示するだけで CI では落ちない状態だった。 - Header / Footer / BlogTag / BlogTagList / BlogCardTag / SpeakerDeck / ReactAriaExampleTable にストーリーを追加。いずれも play 関数で リンク先やアクセシブルネームなど意味のある単位を assert する - addon-a11y を test: "error" に変更。違反が CI で落ちるようになる - BlogCategory の文字色を primary-700 から primary-800 に変更。 opacity-80 と合成すると 3.86:1 でコントラスト比 4.5:1 を満たさなかった。 primary-800 なら合成後も 4.83:1 になる。ダークモードは primary-300 の 合成後が 5.23:1 で元から基準を満たしているため変更しない ストーリーを追加しなかったものと理由: - ClientLayout: useViewPort を呼んで children を返すだけで検証対象の UI が無い - ContentLinkCard: fetch を行う非同期サーバーコンポーネントで、 モックなしではストーリーにできない - ReactAriaExampleStepper: どこからも import されていない。記事中の コードフェンスに同名の実装が載っているだけで、実際には未使用 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKrW7WAzi6MW9KAkLA4mnr
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
削除しました (e43744f)。
「記事に埋め込むためのコンポーネントにはストーリーを書かない」という基準を AGENTS.md にも追記しています。SpeakerDeck / ContentLinkCard / ReactAriaExampleTable を例として挙げました。同じものがまた追加されるのを防ぐためです。
Generated by Claude Code
There was a problem hiding this comment.
同じ基準に当てはまるので、こちらも削除しました (e43744f)。SpeakerDeck は mdx-components.tsx に登録されて記事から使われるだけで、サイトの UI には出てきません。
一点だけ捨てたものを書き残しておきます。このストーリーは iframe の title がアクセシブルネームになること、title を渡さなくても既定値でネームが空にならないことを確認していました。a11y のチェックが効く箇所ではあります。
とはいえ props が 3 つだけの薄いラッパーで、壊れるとしたら記事の表示で気づくと思うので、基準を優先して消す判断で良いと考えています。もし残したければ言ってください。
Generated by Claude Code
ReactAriaExampleTable と SpeakerDeck はサイトの UI ではなく記事に埋め込む ためのコンポーネントなので、Storybook に並べる意味が薄い。 判断基準を AGENTS.md にも書き、同じものが再度追加されないようにした。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKrW7WAzi6MW9KAkLA4mnr
概要
ストーリーが 14 コンポーネント中 4 つにしかなく、
@storybook/addon-a11yもtest: "todo"で違反を表示するだけで CI では落ちない状態でした。カバー範囲を広げたうえで、アクセシビリティを実際のゲートにします。見つかった a11y 違反
ゲート化にあたり既存ストーリーで先に測ったところ、本物の違反が 1 件ありました。
BlogCategoryのtext-primary-700にopacity-80がかかり、合成後の実効色が薄くなって基準を割っていました。パレット自体の問題ではなく不透明度との組み合わせが原因です。計算した選択肢はこうでした。
primary-700+opacity-80(現状)#c46445primary-800+opacity-80#a75a48primary-700(opacity なし)#b63f18opacity-80を外すと「ホバーで濃くなる」という意図が失われるため、primary-800に変更する形を選びました。ダークモードはprimary-300の合成後が 5.23:1 で元から基準を満たしているので触っていません。変更内容
ストーリーを 7 件追加
いずれも
play関数でリンク先やアクセシブルネームなど意味のある単位を assert しています。BlogTagのUnknownTagは「TAGSに無いタグは key をそのまま表示する」挙動、BlogTagListのTagSelectedWithCategoryは「タグを切り替えてもカテゴリが保たれる」クエリ構築を確認しています。a11y をゲート化
.storybook/preview.tsxのa11y.testを"todo"から"error"に変更しました。以降は違反があるとpnpm test:storybookが落ちます。ストーリーを追加しなかったもの
useViewPort()を呼んでchildrenを返すだけで、検証対象の UI がありませんfetchを行う非同期サーバーコンポーネントで、モックなしではストーリーにできませんreact-aria-slots.mdxに同名の実装がコードフェンス内で載っているだけで、コンポーネント本体は未使用です最後のものは削除候補ですが、判断が要るので今回は触っていません。
動作確認
pnpm lintpnpm lint:csspnpm format:checkpnpm typecheckpnpm buildpnpm build-storybookpnpm test:unitpnpm test:storybookpnpm test:e2etest:storybookは 2 回連続で通ることを確認しています。🤖 Generated with Claude Code
https://claude.ai/code/session_01LKrW7WAzi6MW9KAkLA4mnr
Generated by Claude Code