Communitygithub.com

deputychinh/frontend

Vue 3またはReactのWebアプリで、ユーザーが見たりクリックしたりするもの(画面、コンポーネント、コンポーザブル/フック、ストア、フォーム、ルート、データ取得)を追加・変更するときに使用します。小さな修正や「UI」「ページ」「フォーム」「フロントエンド」「ボタンが動かない」といったリクエストにも対応します。

frontend とは?

このスキルは、Vue 3またはReactベースのフロントエンド開発において、ユーザーインターフェースの追加・変更を体系的に行うための包括的なルールとワークフローを提供します。具体的には、リポジトリの規約やデザインシステムの読み込み、データレイヤーやルーターの検出、状態テーブルの作成、テスト駆動開発、アクセシビリティ対応、i18n、CSS設計、Storybook、E2Eテストまでをカバーしています。開発は、まずCLAUDE.mdやdocs/conventions.mdなどのドキュメントを読み込み、フレームワークに応じたパターン(Vue: vue-patterns.md, React: react-patterns.md)に従いながら、機能フォルダ内でサービス→コンポーザブル/フック→コンポーネント→ルートビューの順に構築します。また、各ビューに対してローディング・空・エラー・成功の4状態を必ずテストし、lint、型チェック、単体テスト、Playwrightによるビジュアルリグレッションとアクセシビリティチェックを通過するまで「完了」とは見なさない厳格な品質基準が特徴です。主要なプラットフォーム(Vue 3 + Vite、React + Vite、Nuxt、Next.js)に対応し、コンポーネントライブラリやCSS設計は別スキル(css-design)と連携します。

対応✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/deputychinh/Skills/tree/HEAD/skills/frontend

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

frontend — すべてのユーザー、すべての状態で動作し、変更しやすい画面を実現する

まずリポジトリの CLAUDE.md、docs/conventions.md、docs/design-system.md に従ってください。このスキルはそれらを補完し、上書きしません。ビジュアルに関する作業(トークン、レイアウト、色、CSS)は css-design に属します。ユーザーとしてアプリを実行し、最終的な判断を下すのは verify-app の役割です。

スタックを選ぶ。 Vue 3 と React はどちらも第一級のサポートで、同じルールに従います。リポジトリで既に使われているフレームワークを使用してください。新しいアプリの場合は、デリバリープロファイルのフロントエンドデフォルト(リポジトリのREADMEを参照)を使用し、それ以外はトリアージ時に一度質問します。Vue: TypeScript + Vite(npm create vue@latest -- --ts --router --pinia --vitest --playwright --eslint --prettier)+ references/vue-patterns.md。React: TypeScript + Vite(またはリポジトリのメタフレームワーク)+ references/react-patterns.md。1つのアプリでフレームワークを混在させないでください。SEOのためにSSRまたはSSGが必要ですか?ADR(architect)にNuxtまたはNext.jsを記録してください。このスキルはどちらの場合もクライアントサイド部分をカバーします。

ワークフロー

  1. リポジトリの規約とインベントリを読み込む。 CLAUDE.md、docs/conventions.md、docs/design-system.md を読み、再利用可能な部品(ls src/components src/features src/composables src/hooks src/stores)を一覧表示します。データレイヤー、ルーター、フォーム、i18nライブラリを検出します(Vue: grep -E '"(@tanstack/vue-query|pinia|vue-router|vee-validate|vue-i18n)"' package.json; React: grep -E '"(@tanstack/react-query|@tanstack/react-router|react-router|react-hook-form|i18next|react-intl)"' package.json)。2番目のデータレイヤー、ルーター、ストア、フォーム、i18nライブラリを追加しないでください。証拠: ストーリーのメモに、再利用されたコンポーネントとコンポーザブル/フック、および新しいものを、理由とともにリストします。
  2. コードの前に状態テーブルを書く。 各ビューについて: すべてのデータとその所在 — サーバー(クエリ)、URL(ルートパラメータ/検索)、共有クライアント(Piniaストア、Reactコンテキスト、またはリポジトリのストア)、ローカル(ref/useState)、派生(computed/レンダー内のconst)、外部(ブラウザAPIをラップするコンポーザブルまたはuseSyncExternalStore)。次に、4つの状態(ローディング、空、エラー、成功)とそれぞれのコピーキーをリストします。証拠: ストーリー内の表。
  3. 最初に失敗するテストを書く。 Vitest + @testing-library/vue または @testing-library/react、userEvent.setup()、ロールとアクセシブル名によるクエリ、テストごとに1つの振る舞い、ネットワークはサービス境界でフェイク(MSWまたはsrc/test-utils内のリポジトリのフェイククライアント)。各状態、キーボードパス、1つのネガティブケースをカバーします。証拠: 実行結果が正しい理由で赤色になること。
  4. 機能フォルダ内でビルドする。 src/features/<name>/services(型付けされたAPIコール、フレームワークなし)→ composables/hooks(useQuery/useMutationとキーファクトリをラップするuseX)→ components(<script setup lang="ts"> 付きの Name.vue、または Name.tsx)→ それらを構成するルートビュー。Propsとイベントは型付けされ、anyなし、非null!なし、@/インポート。証拠: git diff --stat が機能フォルダと意図的な共有変更の範囲内に収まっていること。
  5. コピーとフォーマット。 表示されるすべての文字列は、ロケールファイル(vue-i18n useI18n()、またはReactリポジトリのi18nライブラリまたはコピーモジュール)から取得し、すべてのロケールで同じキーを使用します。テストでパリティを強制します。数値と日付は、i18nフォーマッターまたはIntlを使用します。証拠: パリティテストが緑色になること。ハードコードされたコピーがないこと(lintで強制: @intlify/vue-i18n/no-raw-text; React: react/jsx-no-literalsまたはi18nプラグインのルール) — lintの終了コードを引用します。
  6. css-design でスタイルを設定する。 トークンのみ、BEMブロック = コンポーネント名、すべてのスタイルが各状態に対応。.vue スタイルは postcss-html を通じてlintされます。証拠: Stylelintが緑色。
  7. ストーリー(コンポーネントライブラリ)。 各状態に1つのストーリー、両方のテーマ。Storybookのaxeが合格すること。証拠: ストーリーのテストとaxeの実行。
  8. ゲート、終了コードで連鎖。 npm run lint && npm run type-check && npm run test:unit -- --run(Vue: vue-tsc --build; React: tsc -b)、またはmake ci。tailからの出力で判断しないでください。証拠: 終了コード0、カバレッジがリポジトリのしきい値以上。
  9. 本番ビルドに対して実際のブラウザでジャーニーを実行する。 npm run build して出力を提供する(vite preview、またはリポジトリが使用する静的サーバー)。@axe-core/playwright を使用してメインパス用のPlaywrightスペックを追加または拡張します(タグ wcag2a wcag2aa wcag21aa wcag22aa、深刻度と重大度 = 0)。docs/delivery/<slug>/evidence/<view>-390.png および -1280.png をキャプチャし、アプリにダークテーマがある場合はダークテーマもキャプチャします。引用する前にすべてのスクリーンショットを開いて確認します。証拠: スペックが緑色、PNGファイル。
  10. キーボードとスクリーンリーダーの合格。 新しいビューをTabキーで移動する: 順序、可視フォーカス、Escapeキーでオーバーレイを閉じてフォーカスをトリガーに戻す。ナビゲーション後、フォーカスが新しい見出しに移動し、タイトルが変更される。スクリーンリーダー(VoiceOver Cmd+F5、またはNVDA)で名前とアナウンスメントをスポットチェックする。証拠: ストーリーのQAセクションに3行のメモ。
  11. ハンドオーバー。 verify-app を実行してエンドツーエンドの主張を確認する。deliver-loop 内で独立したレビューがアイテムを評価する。ここで彼らの手順を再記述しないでください。

コアチェックリスト

  1. 派生できるものは保存しない: computed(React: レンダー内のconst)を使用する。他の状態を設定するだけのウォッチャーやエフェクトは使用しない。
  2. ウォッチャーとエフェクトは外部(DOM API、サブスクリプション、タイマー、サードパーティウィジェット)とのみ同期し、クリーンアップする(onWatcherCleanup/onUnmounted; React: エフェクトの戻り値)。ユーザーアクションによって引き起こされる作業はイベントハンドラーで実行する。
  3. サーバーデータをローカル状態やストアにコピーしない。クエリキーはフェッチが使用するすべての入力を含める(Vueではrefまたはgetterとして、変更時に再フェッチされるようにする)。
  4. ユーザーが共有したりリロードしたりする選択、フィルター、タブ、ページネーションはURLクエリに配置する。
  5. すべてのデータビューはローディング、空、エラー(再試行付き)、成功をレンダリングし、テストは4つすべてをカバーする。
  6. すべての入力には可視の <label for> がある(両方のフレームワークで useId() からのID)。エラーは aria-describedby で関連付けられたテキスト、入力は保持され、送信時にフォーカスが最初の無効なフィールドに移動する。
  7. ネイティブ要素を優先する(button、a href/ルーターリンク、dialog、details、input type)。div @click/div onClick は使用しない。
  8. カスタムウィジェットは対応するWAI-ARIA APGパターンに従い、キーボードも含む。
  9. フォーカスを移動しない非同期結果は、一度だけレンダリングされ空の role="status"/role="alert" リージョンを介してアナウンスする(Vue: bodyにテレポート; React: ビューより長生きする必要がある場合はポータル)。
  10. ルート変更: document.title を更新し、フォーカスをページの h1 または main に移動する(Vue: router.afterEach + テンプレートref; React: ルーターの同等のもの)。
  11. エラーはルートごとおよびリスクのあるウィジェットごとにキャッチする(Vue: onErrorCaptured 境界コンポーネント; React: エラーバウンダリ)。再試行を表示し、空白のページにしない。
  12. ハードコードされた表示文字列は使用しない。すべてのロケールが同じキーを持つ。
  13. テストはロール/ラベル/テキストでクエリし、user-event で操作し、クラス名、コンポーネント内部(wrapper.vm、状態値)、またはコンポーザブル/フックの呼び出しをアサートしない。
  14. 使用直前にDOMPurifyでサニタイズしない限り、v-html/dangerouslySetInnerHTML は使用しない。データからのURLは https:/mailto:/相対パスに許可リスト化する。
  15. shallowRef/markRaw/v-memo/KeepAlive(React: memo/useMemo/useCallback)は、プロファイラーの計測または文書化された必要性がある場合のみ使用する。
  16. チャートとデータ可視化には、アクセシブルな名前と要約、データテーブル(またはテキスト)の代替、色覚障害者に安全なパレット(シリーズはラベル、形状、パターンでも区別する)、すべてのインタラクションへのキーボードアクセスを用意する。リポジトリが使用しているチャートライブラリに従う。

アンチパターン

Vue:

  • watch(items, v => { filtered.value = v.filter(...) }) — computed を使用する。
  • const { count } = reactive(state) または const { name } = store — リアクティビティが失われる。toRefs/storeToRefs を使用するか、ref を優先する。
  • watch(props.id, ...) または分割されたpropをコンポーザブルに渡す — getter () => id を渡す。
  • propまたは useQuery データを変更する。クエリデータを直接 v-model にバインドする — フォームにコピーする。
  • 同じ要素に v-if と v-for を使用する。並び替え可能なリストで :key="index" を使用する。
  • onWatcherCleanup の前、またはコンポーザブルでライフサイクルフックを登録する前に await を使用する。
  • ルートコンポーネントに defineAsyncComponent を使用する(ルートは () => import(...) を直接受け取る)。
  • ナビゲーションガードで next() を使用する — false、ロケーション、または何も返さない。
  • wrapper.vm にアクセスしたり、setData を呼び出したりするテスト。

React:

  • useEffect(() => setFiltered(items.filter(...)), [items]) — レンダー内で計算する。
  • IDが変更されたときにエフェクトでフォームをリセットする — key={id} を渡す。
  • 条件やループ内でフックを呼び出す。状態を置き換える代わりにその場で変更する。
  • 並び替え可能なリストで key={index} を使用する。計測された理由でメモ化された子に、毎回のレンダーで新しいオブジェクトや関数のpropを渡す。

両方:

  • キャンセル、キャッシュ、再試行、エラーブランチなしでマウント時にフェッチする — データレイヤーを使用する。
  • 空またはエラーブランチのないスピナー。エラーを console.log で握りつぶす。
  • 毎日変化するデータに対して staleTime を0のままにするため、フォーカスするたびに再フェッチされる。
  • スナップショット、ロールバック、確定時の無効化なしの楽観的更新。
  • プレースホルダーをラベルとして使用する。エラーを示すために色だけに頼る。キーなしの role="button"。
  • いたるところで getByTestId を使用する。ページ全体のスナップショット。実装の詳細をテストする。
  • すべてを最初のルートチャンクにプルする機能バレルファイル。
  • スキームをチェックせずに :href="user.url"/href={user.url} を使用する。

「完了」のエビデンス

  • 各状態とキーボードパスに対する、失敗してから合格する単体テスト。
  • Lint、vue-tsc または tsc、テストを連鎖し、終了コード0。カバレッジがしきい値以上。
  • 本番ビルドに対するPlaywrightジャーニー、axeの深刻度/重大度 = 0。
  • docs/delivery/<slug>/evidence/ 内の390pxと1280pxのスクリーンショット(およびダークテーマ)、それぞれ開いて確認済み。
  • ストーリーのQAセクション内のキーボードとスクリーンリーダーのメモ。
  • 次に verify-app。

参考文献

  • references/vue-patterns.md — Vueチェックリスト: SFCおよびコンポーネントAPI、リアクティビティの落とし穴、computed vs watch、コンポーザブル、Pinia、TanStack Query for Vue、Vue Router(URL状態、ガード、フォーカス)、フォーム、i18n、アクセシビリティの詳細、パフォーマンス、セキュリティ、ツール。各項目に出典あり。
  • references/react-patterns.md — Reactチェックリスト: 同じトピックに加え、静的エクスポート(Next.js / Nuxt generate)のバリアント。
  • references/a11y-and-testing.md — UIコードのためのWCAG 2.2 AA項目、ARIA/APG、キーボードとライブリージョン、チャート、VueとReactのためのTesting Library、MSW、Playwright + axe、テストすべきでないもの。

関連スキル