Communitygithub.com

フロントエンドデザイナー

デザインシステムのベストプラクティス、モダンCSS、フレームワーク非依存のパターンを用いて、アクセシブルでレスポンシブ、高性能なフロントエンドコンポーネントを構築します。

フロントエンドデザイナー とは?

このスキルは、フロントエンドデザイナーや開発者が、アクセシビリティ、パフォーマンス、メンテナンス性に優れたモダンなユーザーインターフェースを構築するための包括的なガイドです。WCAG 2.1 AA準拠のアクセシブルなコンポーネント設計、モバイルファーストなレスポンシブデザイン、デザイントークンに基づいた一貫性のあるスタイリング、そしてAtomic Designに沿ったコンポーネント駆動開発の原則を提供します。React、Vue、Svelteなど、フレームワークに依存しないパターンで記述されており、Claude Code、Cursor、CodexなどのAIエージェントがコード生成や設計支援を行う際に、ベストプラクティスに従った高品質なアウトプットを実現します。具体的なコンポーネント例(Button、Card、Form Input)と、レスポンシブデザイン、アクセシビリティ、パフォーマンス最適化の具体的な実装パターンが含まれており、AIエージェントはこれを基に、設計システムに沿った一貫性のあるコードを生成できます。

対応✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/Samueladewole/claude-skills/tree/HEAD/frontend-designer

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

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

ドキュメント

フロントエンドデザイナー は何をしますか?

モダンなベストプラクティスを用いて、美しく、アクセシブルで、高性能なユーザーインターフェースを構築するための、フロントエンドデザイナーと開発者のための包括的なスキルです。

このスキルの機能

フロントエンドデザイナー/開発者を以下で支援します:

  • コンポーネント設計と開発 - 再利用可能でアクセシブルなコンポーネントの構築
  • デザインシステム - トークン、パターン、ドキュメントの実装
  • レスポンシブデザイン - モバイルファースト、流動的なレイアウト
  • アクセシビリティ (WCAG 2.1) - インクルーシブデザインパターン
  • モダンCSS - Flexbox、Grid、カスタムプロパティ
  • パフォーマンス最適化 - 高速で効率的なフロントエンド
  • フレームワークパターン - React、Vue、Svelteのベストプラクティス
  • デザインからコードへ - Figmaからプロダクションへのワークフロー

このスキルが重要な理由

体系的なアプローチがない場合:

  • コンポーネント実装の不整合
  • アクセシビリティの問題
  • レスポンシブ動作の不備
  • コードとスタイルの重複
  • 保守が難しい
  • パフォーマンスの問題

このスキルを用いた場合:

  • 一貫性のある再利用可能なコンポーネント
  • デフォルトでWCAG AA準拠
  • モバイルファーストなレスポンシブデザイン
  • デザインシステムに準拠
  • 保守性の高いコードベース
  • 高速で最適化された配信

中核原則

1. アクセシビリティファースト

  • WCAG 2.1 AA 最低限
  • セマンティックHTML
  • キーボードナビゲーション
  • スクリーンリーダーサポート
  • フォーカス管理
  • 色のコントラスト

2. モバイルファーストレスポンシブ

  • モバイル (320px) から開始
  • プログレッシブエンハンスメント
  • 流動的なタイポグラフィ
  • 柔軟なレイアウト
  • タッチ操作に適したターゲット

3. デフォルトでのパフォーマンス

  • 最小限のCSS/JS
  • レイジーローディング
  • 最適化された画像
  • クリティカルCSS
  • ツリーシェイキング

4. コンポーネント駆動

  • Atomic Design 方法論
  • 再利用可能なコンポーネント
  • Propsベースのカスタマイズ
  • 継承よりもコンポジション

5. デザインシステム準拠

  • デザイントークン
  • 一貫性のあるスペーシング
  • タイポグラフィスケール
  • カラーパレット
  • コンポーネントライブラリ

コンポーネントパターン

ボタンコンポーネント

アクセシブルで柔軟なボタンパターン:

// React の例
interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  loading?: boolean;
  children: React.ReactNode;
  onClick?: () => void;
}

export const Button: React.FC<ButtonProps> = ({
  variant = 'primary',
  size = 'md',
  disabled = false,
  loading = false,
  children,
  onClick,
  ...props
}) => {
  return (
    <button
      className={`btn btn--${variant} btn--${size}`}
      disabled={disabled || loading}
      onClick={onClick}
      aria-busy={loading}
      {...props}
    >
      {loading ? <Spinner /> : children}
    </button>
  );
};

CSS(デザイントークン使用):

.btn {
  /* ベーススタイル */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);

  font-family: var(--font-sans);
  font-weight: 600;
  text-decoration: none;

  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;

  transition: all 0.2s ease;

  /* アクセシビリティ */
  min-height: 44px; /* WCAG タッチターゲット */

  &:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
}

/* バリアント */
.btn--primary {
  background: var(--color-primary);
  color: var(--color-on-primary);

  &:hover:not(:disabled) {
    background: var(--color-primary-hover);
  }
}

.btn--secondary {
  background: var(--color-secondary);
  color: var(--color-on-secondary);
}

.btn--ghost {
  background: transparent;
  color: var(--color-primary);
  border: 1px solid currentColor;
}

/* サイズ */
.btn--sm {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
}

.btn--md {
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-base);
}

.btn--lg {
  padding: var(--space-4) var(--space-6);
  font-size: var(--text-lg);
}

カードコンポーネント

柔軟でアクセシブルなカード:

interface CardProps {
  variant?: 'elevated' | 'outlined' | 'filled';
  padding?: 'none' | 'sm' | 'md' | 'lg';
  interactive?: boolean;
  children: React.ReactNode;
}

export const Card: React.FC<CardProps> = ({
  variant = 'elevated',
  padding = 'md',
  interactive = false,
  children,
}) => {
  const Component = interactive ? 'button' : 'div';

  return (
    <Component
      className={`
        card
        card--${variant}
        card--padding-${padding}
        ${interactive ? 'card--interactive' : ''}
      `}
      {...(interactive && { role: 'button', tabIndex: 0 })}
    >
      {children}
    </Component>
  );
};

CSS:

.card {
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}

.card--elevated {
  box-shadow: var(--shadow-md);
}

.card--outlined {
  border: 1px solid var(--color-border);
}

.card--filled {
  background: var(--color-surface-variant);
}

.card--interactive {
  cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;

  &:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
  }

  &:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
  }
}

.card--padding-sm { padding: var(--space-3); }
.card--padding-md { padding: var(--space-4); }
.card--padding-lg { padding: var(--space-6); }

フォーム入力コンポーネント

アクセシブルなフォーム入力:

interface InputProps {
  label: string;
  error?: string;
  hint?: string;
  required?: boolean;
  type?: 'text' | 'email' | 'password' | 'number';
}

export const Input: React.FC<InputProps> = ({
  label,
  error,
  hint,
  required = false,
  type = 'text',
  ...props
}) => {
  const id = useId();
  const hintId = `${id}-hint`;
  const errorId = `${id}-error`;

  return (
    <div className="input-wrapper">
      <label htmlFor={id} className="input-label">
        {label}
        {required && <span aria-label="required">*</span>}
      </label>

      {hint && (
        <p id={hintId} className="input-hint">
          {hint}
        </p>
      )}

      <input
        id={id}
        type={type}
        className={`input ${error ? 'input--error' : ''}`}
        aria-required={required}
        aria-invalid={!!error}
        aria-describedby={error ? errorId : hint ? hintId : undefined}
        {...props}
      />

      {error && (
        <p id={errorId} className="input-error" role="alert">
          {error}
        </p>
      )}
    </div>
  );
};

デザイントークン

デザインシステムのためのCSSカスタムプロパティ:

:root {
  /* カラー - プライマリ */
  --color-primary: #0066FF;
  --color-primary-hover: #0052CC;
  --color-on-primary: #FFFFFF;

  /* カラー - サーフェス */
  --color-surface: #FFFFFF;
  --color-surface-variant: #F5F5F5;
  --color-on-surface: #1A1A1A;

  /* カラー - ボーダー */
  --color-border: #E0E0E0;
  --color-border-hover: #BDBDBD;

  /* カラー - セマンティック */
  --color-error: #D32F2F;
  --color-success: #388E3C;
  --color-warning: #F57C00;
  --color-info: #1976D2;

  /* スペーシングスケール (8px ベース) */
  --space-1: 0.25rem;  /* 4px */
  --space-2: 0.5rem;   /* 8px */
  --space-3: 0.75rem;  /* 12px */
  --space-4: 1rem;     /* 16px */
  --space-5: 1.5rem;   /* 24px */
  --space-6: 2rem;     /* 32px */
  --space-8: 3rem;     /* 48px */
  --space-10: 4rem;    /* 64px */

  /* タイポグラフィスケール */
  --text-xs: 0.75rem;    /* 12px */
  --text-sm: 0.875rem;   /* 14px */
  --text-base: 1rem;     /* 16px */
  --text-lg: 1.125rem;   /* 18px */
  --text-xl: 1.25rem;    /* 20px */
  --text-2xl: 1.5rem;    /* 24px */
  --text-3xl: 1.875rem;  /* 30px */
  --text-4xl: 2.25rem;   /* 36px */

  /* フォントファミリー */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono: "SF Mono", Monaco, "Cascadia Code", monospace;

  /* 行の高さ */
  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;

  /* ボーダー半径 */
  --radius-sm: 0.25rem;  /* 4px */
  --radius-md: 0.5rem;   /* 8px */
  --radius-lg: 1rem;     /* 16px */
  --radius-full: 9999px;

  /* シャドウ */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
  --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);

  /* フォーカスリング */
  --color-focus: #0066FF;

  /* トランジション */
  --transition-fast: 150ms ease;
  --transition-base: 200ms ease;
  --transition-slow: 300ms ease;
}

/* ダークモード */
@media (prefers-color-scheme: dark) {
  :root {
    --color-surface: #1A1A1A;
    --color-surface-variant: #2A2A2A;
    --color-on-surface: #FFFFFF;
    --color-border: #3A3A3A;
  }
}

レスポンシブデザインパターン

モバイルファーストブレークポイント

/* モバイルファーストアプローチ */
.container {
  padding: var(--space-4);

  /* タブレット: 768px 以上 */
  @media (min-width: 48rem) {
    padding: var(--space-6);
  }

  /* デスクトップ: 1024px 以上 */
  @media (min-width: 64rem) {
    padding: var(--space-8);
    max-width: 1200px;
    margin: 0 auto;
  }
}

流動的なタイポグラフィ

/* レスポンシブタイポグラフィ */
h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
  line-height: var(--leading-tight);
}

h2 {
  font-size: clamp(1.5rem, 4vw, 2.5rem);
}

p {
  font-size: clamp(1rem, 2vw, 1.125rem);
  line-height: var(--leading-normal);
}

グリッドレイアウト

/* レスポンシブグリッド */
.grid {
  display: grid;
  gap: var(--space-4);

  /* 自動フィットカラム (最小 280px) */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

/* 12カラムグリッドシステム */
.grid-12 {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--space-4);
}

.col-span-4 {
  grid-column: span 4;
}

/* モバイルでスタック */
@media (max-width: 48rem) {
  .col-span-4 {
    grid-column: span 12;
  }
}

アクセシビリティパターン

スキップリンク

export const SkipLink = () => (
  <a href="#main-content" className="skip-link">
    メインコンテンツにスキップ
  </a>
);
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: var(--color-primary);
  color: var(--color-on-primary);
  padding: var(--space-2) var(--space-4);
  text-decoration: none;
  z-index: 100;

  &:focus {
    top: 0;
  }
}

フォーカス管理

// フォーカストラップ付きモーダル
export const Modal = ({ isOpen, onClose, children }) => {
  const modalRef = useRef(null);

  useEffect(() => {
    if (isOpen) {
      // 現在フォーカスされている要素を保存
      const previouslyFocused = document.activeElement;

      // モーダル内の最初のフォーカス可能な要素にフォーカス
      const firstFocusable = modalRef.current?.querySelector(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
      );
      firstFocusable?.focus();

      // 閉じる時にフォーカスを戻す
      return () => previouslyFocused?.focus();
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div
      ref={modalRef}
      role="dialog"
      aria-modal="true"
      className="modal-overlay"
    >
      <div className="modal">
        {children}
      </div>
    </div>
  );
};

ARIAラベル

// アクセシブルなラベル付きアイコンボタン
export const IconButton = ({ icon, label, ...props }) => (
  <button
    aria-label={label}
    className="icon-button"
    {...props}
  >
    <span aria-hidden="true">{icon}</span>
  </button>
);

// ローディング状態
LoadingButton = ({ loading, children, ...props }) => (
  <button
    aria-busy={loading}
    aria-live="polite"
    disabled={loading}
    {...props}
  >
    {loading ? '読み込み中...' : children}
  </button>
);

パフォーマンス最適化

クリティカルCSS

<!-- クリティカルCSSをインライン化 -->
<style>
  /* ファーストビュースタイル */
  body { margin: 0; font-family: var(--font-sans); }
  .header { /* クリティカルなヘッダースタイル */ }
  .hero { /* クリティカルなヒーロースタイル */ }
</style>

<!-- 完全なスタイルシートを非同期で読み込み -->
<link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">

レイジーローディング画像

export const LazyImage = ({ src, alt, ...props }) => (
  <img
    src={src}
    alt={alt}
    loading="lazy"
    decoding="async"
    {...props}
  />
);

コード分割

// React の遅延読み込み
const Dashboard = lazy(() => import('./Dashboard'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <Dashboard />
    </Suspense>
  );
}

モダンCSSテクニック

コンテナクエリ

.card {
  container-type: inline-size;
}

.card-content {
  display: flex;
  flex-direction: column;

  /* コンテナが 400px 以上の場合に行レイアウトに切り替え */
  @container (min-width: 400px) {
    flex-direction: row;
  }
}

CSS Grid 自動フィット

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: var(--space-4);
}

テーマ設定のためのカスタムプロパティ

/* ライトテーマ (デフォルト) */
:root {
  --bg: #ffffff;
  --text: #000000;
}

/* ダークテーマ */
[data-theme="dark"] {
  --bg: #000000;
  --text: #ffffff;
}

body {
  background: var(--bg);
  color: var(--text);
}

コンポーネントライブラリ構成

src/
├── components/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.css
│   │   ├── Button.test.tsx
│   │   └── Button.stories.tsx
│   ├── Card/
│   ├── Input/
│   └── index.ts
├── tokens/
│   ├── colors.css
│   ├── spacing.css
│   ├── typography.css
│   └── index.css
├── utils/
│   ├── a11y.ts
│   └── responsive.ts
└── index.ts

このスキルの使い方

コンポーネント生成

./scripts/generate_component.sh Button

以下のコンポーネントを作成:

  • TypeScript/JSX ファイル
  • CSS モジュール
  • テストファイル
  • Storybook ストーリー
  • アクセシビリティチェック

デザインシステムセットアップ

./scripts/setup_design_system.sh

作成:

  • デザイントークン (CSS カスタムプロパティ)
  • ベーススタイル
  • コンポーネントテンプレート
  • ドキュメント構成

アクセシビリティ監査

./scripts/audit_accessibility.sh

チェック:

  • 色のコントラスト比
  • キーボードナビゲーション
  • ARIA 属性
  • セマンティックHTML
  • フォーカス管理

ベストプラクティス

コンポーネント設計

✅ 推奨事項:

  • セマンティックHTMLを使用する
  • コンポーネントをキーボード操作可能にする
  • ARIAラベルを提供する
  • ライトモードとダークモードの両方をサポートする

…

[truncated for translation]

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

Samueladewole/claude-skills

Create API handoff documentation for frontend developers. Use when backend work is complete and needs to be documented for frontend integration, or user says 'create handoff', 'document API', 'frontend handoff', or 'API documentation'.

Samueladewole/claude-skills

Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling

Samueladewole/claude-skills

Document frontend data needs for backend developers. Use when frontend needs to communicate API requirements to backend, or user says 'backend requirements', 'what data do I need', 'API requirements', or is describing data needs for a UI.

Samueladewole/claude-skills

Master Next.js 14+ App Router with Server Components, streaming, parallel routes, and advanced data fetching. Use when building Next.js applications, implementing SSR/SSG, or optimizing React Server Components.

Samueladewole/claude-skills

Analyze a short-form video (TikTok, Instagram Reel, YouTube Short, or any MP4) to extract its full transcript, understand why it performed, and generate concrete repurposing ideas. Use this skill whenever the user asks to: analyze a video, understand why something went viral, reverse-engineer a creator's content, extract a transcript from a video, figure out what makes a video perform, or get repurposing ideas from a video. Also trigger when the user says things like "why did this perform", "what's the hook here", "can I repurpose this", "analyze this TikTok/Reel", or drops a video file path or URL.

関連スキル