jpskill.com
🛠️ 開発・MCP コミュニティ

frontend-ui-engineering

ユーザーインターフェースを構築・修正する際に、コンポーネント作成、レイアウト実装、状態管理を行い、AI生成ではなく高品質で洗練された見た目と操作感を実現するSkill。

📜 元の英語説明(参考)

Use when building or modifying user-facing interfaces. Use when creating components, implementing layouts, managing state, or when the output needs to look and feel production-quality rather than AI-generated.

🇯🇵 日本人クリエイター向け解説

一言でいうと

ユーザーインターフェースを構築・修正する際に、コンポーネント作成、レイアウト実装、状態管理を行い、AI生成ではなく高品質で洗練された見た目と操作感を実現するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o frontend-ui-engineering.zip https://jpskill.com/download/9581.zip && unzip -o frontend-ui-engineering.zip && rm frontend-ui-engineering.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/9581.zip -OutFile "$d\frontend-ui-engineering.zip"; Expand-Archive "$d\frontend-ui-engineering.zip" -DestinationPath $d -Force; ri "$d\frontend-ui-engineering.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して frontend-ui-engineering.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → frontend-ui-engineering フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

フロントエンド UI エンジニアリング

概要

アクセス可能で、パフォーマンスが高く、視覚的に洗練された、プロダクション品質のユーザーインターフェースを構築します。目標は、AI によって生成されたような UI ではなく、トップ企業のデザインを意識したエンジニアによって構築されたように見える UI を実現することです。これは、実際のデザインシステムへの準拠、適切なアクセシビリティ、思慮深いインタラクションパターン、そして一般的な「AI 美学」がないことを意味します。

使用場面

  • 新しい UI コンポーネントまたはページの構築
  • 既存のユーザーインターフェースの修正
  • レスポンシブなレイアウトの実装
  • インタラクティビティまたは状態管理の追加
  • 視覚的または UX の問題の修正

コンポーネントアーキテクチャ

ファイル構造

コンポーネントに関連するすべてのものを同じ場所に配置します。

src/components/
  TaskList/
    TaskList.tsx          # コンポーネントの実装
    TaskList.test.tsx     # テスト
    TaskList.stories.tsx  # Storybook のストーリー (使用する場合)
    use-task-list.ts      # カスタムフック (複雑な状態の場合)
    types.ts              # コンポーネント固有の型 (必要な場合)

コンポーネントパターン

設定よりもコンポジションを優先します。

// 良い例: コンポーザブル
<Card>
  <CardHeader>
    <CardTitle>タスク</CardTitle>
  </CardHeader>
  <CardBody>
    <TaskList tasks={tasks} />
  </CardBody>
</Card>

// 避けるべき例: 過剰な設定
<Card
  title="タスク"
  headerVariant="large"
  bodyPadding="md"
  content={<TaskList tasks={tasks} />}
/>

コンポーネントの焦点を絞ります。

// 良い例: 1つのことを行う
export function TaskItem({ task, onToggle, onDelete }: TaskItemProps) {
  return (
    <li className="flex items-center gap-3 p-3">
      <Checkbox checked={task.done} onChange={() => onToggle(task.id)} />
      <span className={task.done ? 'line-through text-muted' : ''}>{task.title}</span>
      <Button variant="ghost" size="sm" onClick={() => onDelete(task.id)}>
        <TrashIcon />
      </Button>
    </li>
  );
}

データ取得とプレゼンテーションを分離します。

// コンテナ: データを処理する
export function TaskListContainer() {
  const { tasks, isLoading, error } = useTasks();

  if (isLoading) return <TaskListSkeleton />;
  if (error) return <ErrorState message="タスクのロードに失敗しました" retry={refetch} />;
  if (tasks.length === 0) return <EmptyState message="まだタスクはありません" />;

  return <TaskList tasks={tasks} />;
}

// プレゼンテーション: レンダリングを処理する
export function TaskList({ tasks }: { tasks: Task[] }) {
  return (
    <ul role="list" className="divide-y">
      {tasks.map(task => <TaskItem key={task.id} task={task} />)}
    </ul>
  );
}

状態管理

動作する最も単純なアプローチを選択します。

ローカル状態 (useState)           → コンポーネント固有の UI 状態
リフトされた状態                     → 2〜3 個の兄弟コンポーネント間で共有
Context                          → テーマ、認証、ロケール (読み込みが多く、書き込みが少ない)
URL 状態 (searchParams)         → フィルター、ページネーション、共有可能な UI 状態
サーバー状態 (React Query, SWR)  → キャッシュされたリモートデータ
グローバルストア (Zustand, Redux)    → アプリ全体で共有される複雑なクライアント状態

プロパティの受け渡しを 3 レベルより深くしないでください。 使用しないコンポーネントを介してプロパティを渡している場合は、コンテキストを導入するか、コンポーネントツリーを再構築します。

デザインシステムへの準拠

AI 美学を避ける

AI によって生成された UI には、認識可能なパターンがあります。それらをすべて避けてください。

AI のデフォルト プロダクション品質
すべてが紫/インディゴ プロジェクトの実際の色パレットを使用する
過剰なグラデーション デザインシステムに一致するフラットまたは微妙なグラデーション
すべてが丸い (rounded-2xl) デザインシステムからの、一貫した border-radius
一般的なヒーローセクション コンテンツファーストのレイアウト
Lorem ipsum スタイルのコピー リアルなプレースホルダーコンテンツ
すべてに特大のパディング 一貫したスペーシングスケール
ストックカードグリッド 目的主導のレイアウト
影の多いデザイン デザインシステムで指定されていない限り、微妙な影または影なし

スペーシングとレイアウト

一貫したスペーシングスケールを使用します。値を勝手に作成しないでください。

/* スケールを使用する: 0.25rem 刻み (またはプロジェクトで使用しているもの) */
/* 良い例 */  padding: 1rem;      /* 16px */
/* 良い例 */  gap: 0.75rem;       /* 12px */
/* 悪い例 */   padding: 13px;      /* スケールにない */
/* 悪い例 */   margin-top: 2.3rem; /* スケールにない */

タイポグラフィ

タイプ階層を尊重します。

h1 → ページタイトル (1 ページに 1 つ)
h2 → セクションタイトル
h3 → サブセクションタイトル
body → デフォルトテキスト
small → セカンダリ/ヘルパーテキスト

見出しレベルをスキップしないでください。見出しスタイルを見出し以外のコンテンツに使用しないでください。

  • セマンティックカラートークンを使用する: text-primarybg-surfaceborder-default — 生の 16 進数値ではない
  • 十分なコントラストを確保する (通常のテキストの場合は 4.5:1、大きなテキストの場合は 3:1)
  • 色だけに頼って情報を伝えない (アイコン、テキスト、またはパターンも使用する)

アクセシビリティ (WCAG 2.1 AA)

すべてのコンポーネントは、これらの標準を満たす必要があります。

キーボードナビゲーション

// すべてのインタラクティブな要素はキーボードでアクセス可能である必要があります
<button onClick={handleClick}>クリック</button>        // ✓ デフォルトでフォーカス可能
<div onClick={handleClick}>クリック</div>               // ✗ フォーカス可能ではない
<div role="button" tabIndex={0} onClick={handleClick}    // ✓ ただし、<button> を優先する
     onKeyDown={e => e.key === 'Enter' && handleClick()}>
  クリック
</div>

ARIA ラベル

// 可視テキストがないインタラクティブな要素にラベルを付ける
<button aria-label="ダイアログを閉じる"><XIcon /></button>

// フォーム入力にラベルを付ける
<label htmlFor="email">メール</label>
<input id="email" type="email" />

// または、可視ラベルが存在しない場合は aria-label を使用する
<input aria-label="タスクを検索" type="search" />

フォーカスの管理

// コンテンツが変更されたときにフォーカスを移動する
function Dialog({ isOpen, onClose }: DialogProps) {
  const closeRef = useRef<HTMLButtonElement>(null);

  useEffect(() => {
    if (isOpen) closeRef.current?.focus();
  }, [isOpen]);

  // 開いているときにダイアログ内にフォーカスを閉じ込める
  return (
    <dialog open={isOpen}>
      <button ref={closeRef} onClick={onClose}>閉じる</button>
      {/* ダイアログのコンテンツ */}
    </dialog>
  );
}

M

📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Frontend UI Engineering

Overview

Build production-quality user interfaces that are accessible, performant, and visually polished. The goal is UI that looks like it was built by a design-aware engineer at a top company — not like it was generated by an AI. This means real design system adherence, proper accessibility, thoughtful interaction patterns, and no generic "AI aesthetic."

When to Use

  • Building new UI components or pages
  • Modifying existing user-facing interfaces
  • Implementing responsive layouts
  • Adding interactivity or state management
  • Fixing visual or UX issues

Component Architecture

File Structure

Colocate everything related to a component:

src/components/
  TaskList/
    TaskList.tsx          # Component implementation
    TaskList.test.tsx     # Tests
    TaskList.stories.tsx  # Storybook stories (if using)
    use-task-list.ts      # Custom hook (if complex state)
    types.ts              # Component-specific types (if needed)

Component Patterns

Prefer composition over configuration:

// Good: Composable
<Card>
  <CardHeader>
    <CardTitle>Tasks</CardTitle>
  </CardHeader>
  <CardBody>
    <TaskList tasks={tasks} />
  </CardBody>
</Card>

// Avoid: Over-configured
<Card
  title="Tasks"
  headerVariant="large"
  bodyPadding="md"
  content={<TaskList tasks={tasks} />}
/>

Keep components focused:

// Good: Does one thing
export function TaskItem({ task, onToggle, onDelete }: TaskItemProps) {
  return (
    <li className="flex items-center gap-3 p-3">
      <Checkbox checked={task.done} onChange={() => onToggle(task.id)} />
      <span className={task.done ? 'line-through text-muted' : ''}>{task.title}</span>
      <Button variant="ghost" size="sm" onClick={() => onDelete(task.id)}>
        <TrashIcon />
      </Button>
    </li>
  );
}

Separate data fetching from presentation:

// Container: handles data
export function TaskListContainer() {
  const { tasks, isLoading, error } = useTasks();

  if (isLoading) return <TaskListSkeleton />;
  if (error) return <ErrorState message="Failed to load tasks" retry={refetch} />;
  if (tasks.length === 0) return <EmptyState message="No tasks yet" />;

  return <TaskList tasks={tasks} />;
}

// Presentation: handles rendering
export function TaskList({ tasks }: { tasks: Task[] }) {
  return (
    <ul role="list" className="divide-y">
      {tasks.map(task => <TaskItem key={task.id} task={task} />)}
    </ul>
  );
}

State Management

Choose the simplest approach that works:

Local state (useState)           → Component-specific UI state
Lifted state                     → Shared between 2-3 sibling components
Context                          → Theme, auth, locale (read-heavy, write-rare)
URL state (searchParams)         → Filters, pagination, shareable UI state
Server state (React Query, SWR)  → Remote data with caching
Global store (Zustand, Redux)    → Complex client state shared app-wide

Avoid prop drilling deeper than 3 levels. If you're passing props through components that don't use them, introduce context or restructure the component tree.

Design System Adherence

Avoid the AI Aesthetic

AI-generated UI has recognizable patterns. Avoid all of them:

AI Default Production Quality
Purple/indigo everything Use the project's actual color palette
Excessive gradients Flat or subtle gradients matching the design system
Rounded everything (rounded-2xl) Consistent border-radius from the design system
Generic hero sections Content-first layouts
Lorem ipsum-style copy Realistic placeholder content
Oversized padding everywhere Consistent spacing scale
Stock card grids Purpose-driven layouts
Shadow-heavy design Subtle or no shadows unless the design system specifies

Spacing and Layout

Use a consistent spacing scale. Don't invent values:

/* Use the scale: 0.25rem increments (or whatever the project uses) */
/* Good */  padding: 1rem;      /* 16px */
/* Good */  gap: 0.75rem;       /* 12px */
/* Bad */   padding: 13px;      /* Not on any scale */
/* Bad */   margin-top: 2.3rem; /* Not on any scale */

Typography

Respect the type hierarchy:

h1 → Page title (one per page)
h2 → Section title
h3 → Subsection title
body → Default text
small → Secondary/helper text

Don't skip heading levels. Don't use heading styles for non-heading content.

Color

  • Use semantic color tokens: text-primary, bg-surface, border-default — not raw hex values
  • Ensure sufficient contrast (4.5:1 for normal text, 3:1 for large text)
  • Don't rely solely on color to convey information (use icons, text, or patterns too)

Accessibility (WCAG 2.1 AA)

Every component must meet these standards:

Keyboard Navigation

// Every interactive element must be keyboard accessible
<button onClick={handleClick}>Click me</button>        // ✓ Focusable by default
<div onClick={handleClick}>Click me</div>               // ✗ Not focusable
<div role="button" tabIndex={0} onClick={handleClick}    // ✓ But prefer <button>
     onKeyDown={e => e.key === 'Enter' && handleClick()}>
  Click me
</div>

ARIA Labels

// Label interactive elements that lack visible text
<button aria-label="Close dialog"><XIcon /></button>

// Label form inputs
<label htmlFor="email">Email</label>
<input id="email" type="email" />

// Or use aria-label when no visible label exists
<input aria-label="Search tasks" type="search" />

Focus Management

// Move focus when content changes
function Dialog({ isOpen, onClose }: DialogProps) {
  const closeRef = useRef<HTMLButtonElement>(null);

  useEffect(() => {
    if (isOpen) closeRef.current?.focus();
  }, [isOpen]);

  // Trap focus inside dialog when open
  return (
    <dialog open={isOpen}>
      <button ref={closeRef} onClick={onClose}>Close</button>
      {/* dialog content */}
    </dialog>
  );
}

Meaningful Empty and Error States

// Don't show blank screens
function TaskList({ tasks }: { tasks: Task[] }) {
  if (tasks.length === 0) {
    return (
      <div role="status" className="text-center py-12">
        <TasksEmptyIcon className="mx-auto h-12 w-12 text-muted" />
        <h3 className="mt-2 text-sm font-medium">No tasks</h3>
        <p className="mt-1 text-sm text-muted">Get started by creating a new task.</p>
        <Button className="mt-4" onClick={onCreateTask}>Create Task</Button>
      </div>
    );
  }

  return <ul role="list">...</ul>;
}

Responsive Design

Design for mobile first, then expand:

// Tailwind: mobile-first responsive
<div className="
  grid grid-cols-1      /* Mobile: single column */
  sm:grid-cols-2        /* Small: 2 columns */
  lg:grid-cols-3        /* Large: 3 columns */
  gap-4
">

Test at these breakpoints: 320px, 768px, 1024px, 1440px.

Loading and Transitions

// Skeleton loading (not spinners for content)
function TaskListSkeleton() {
  return (
    <div className="space-y-3" aria-busy="true" aria-label="Loading tasks">
      {Array.from({ length: 3 }).map((_, i) => (
        <div key={i} className="h-12 bg-muted animate-pulse rounded" />
      ))}
    </div>
  );
}

// Optimistic updates for perceived speed
function useToggleTask() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: toggleTask,
    onMutate: async (taskId) => {
      await queryClient.cancelQueries({ queryKey: ['tasks'] });
      const previous = queryClient.getQueryData(['tasks']);

      queryClient.setQueryData(['tasks'], (old: Task[]) =>
        old.map(t => t.id === taskId ? { ...t, done: !t.done } : t)
      );

      return { previous };
    },
    onError: (_err, _taskId, context) => {
      queryClient.setQueryData(['tasks'], context?.previous);
    },
  });
}

Common Rationalizations

Rationalization Reality
"Accessibility is a nice-to-have" It's a legal requirement in many jurisdictions and an engineering quality standard.
"We'll make it responsive later" Retrofitting responsive design is 3x harder than building it from the start.
"The design isn't final, so I'll skip styling" Use the design system defaults. Unstyled UI creates a broken first impression for reviewers.
"This is just a prototype" Prototypes become production code. Build the foundation right.
"The AI aesthetic is fine for now" It signals low quality. Use the project's actual design system from the start.

Red Flags

  • Components with more than 200 lines (split them)
  • Inline styles or arbitrary pixel values
  • Missing error states, loading states, or empty states
  • No keyboard navigation testing
  • Color as the sole indicator of state (red/green without text or icons)
  • Generic "AI look" (purple gradients, oversized cards, stock layouts)

Verification

After building UI:

  • [ ] Component renders without console errors
  • [ ] All interactive elements are keyboard accessible (Tab through the page)
  • [ ] Screen reader can convey the page's content and structure
  • [ ] Responsive: works at 320px, 768px, 1024px, 1440px
  • [ ] Loading, error, and empty states all handled
  • [ ] Follows the project's design system (spacing, colors, typography)
  • [ ] No accessibility warnings in dev tools or axe-core