mcpField
F
frontend-designer

frontend-designer

github.com/jamesrochabrunデザイン・UI221337 回インストール41 週間前に更新
リポジトリ

frontend-designer について

frontend-designerは、フロントエンドのデザイナーや開発者向けのUI構築スキルです。コンポーネント、デザインシステム、レスポンシブ設計、アクセシビリティ、パフォーマンスを扱い、生成や監査のスクリプトも備えています。

主な機能

  1. 01再利用可能でアクセシブルなコンポーネント構築
  2. 02デザイントークンに基づくシステム構築
  3. 03モバイルファーストのレスポンシブレイアウト
  4. 04React、Vue、Svelteのパターン活用
  5. 05フロントエンドのパフォーマンス最適化
  6. 06コンポーネント生成とアクセシビリティ監査

活用シーン

  1. 01キーボード操作に対応したUIコンポーネントの作成
  2. 02プロジェクト全体のデザイントークン統一
  3. 03モバイルから大画面までのページ調整
  4. 04色のコントラストとセマンティックHTMLの確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jamesrochabrun/skills --skill frontend-designer

インストール後、Claude Code で/frontend-designerを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
accessibility_checklist.md
component_library.md
design_tokens.md
responsive_patterns.md
scripts/
audit_accessibility.sh
generate_component.sh
setup_design_system.sh

詳細

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

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

このスキルでできること

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

  • コンポーネントの設計と開発 - 再利用可能でアクセシブルなコンポーネントを構築します
  • デザインシステム - トークン、パターン、ドキュメントを実装します
  • レスポンシブデザイン - モバイルファーストの流動的なレイアウトを実現します
  • アクセシビリティ(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. コンポーネント駆動
  • アトミックデザインの手法
  • 再利用可能なコンポーネント
  • Props ベースのカスタマイズ
  • 継承よりコンポジションを優先
5. デザインシステムとの整合性
  • デザイントークン
  • 一貫した余白
  • タイポグラフィのスケール
  • カラーパレット
  • コンポーネントライブラリ

コンポーネントのパターン

Button コンポーネント

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

// React example
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 {
  /* Base styles */
  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;

  /* Accessibility */
  min-height: 44px; /* WCAG touch target */

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

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

/* Variants */
.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;
}

/* Sizes */
.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);
}
Card コンポーネント

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

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); }
Form Input コンポーネント

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

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 {
  /* Colors - Primary */
  --color-primary: #0066FF;
  --color-primary-hover: #0052CC;
  --color-on-primary: #FFFFFF;

  /* Colors - Surface */
  --color-surface: #FFFFFF;
  --color-surface-variant: #F5F5F5;
  --color-on-surface: #1A1A1A;

  /* Colors - Borders */
  --color-border: #E0E0E0;
  --color-border-hover: #BDBDBD;

  /* Colors - Semantic */
  --color-error: #D32F2F;
  --color-success: #388E3C;
  --color-warning: #F57C00;
  --color-info: #1976D2;

  /* Spacing Scale (8px base) */
  --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 */

  /* Typography Scale */
  --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 Families */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono: "SF Mono", Monaco, "Cascadia Code", monospace;

  /* Line Heights */
  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;

  /* Border Radius */
  --radius-sm: 0.25rem;  /* 4px */
  --radius-md: 0.5rem;   /* 8px */
  --radius-lg: 1rem;     /* 16px */
  --radius-full: 9999px;

  /* Shadows */
  --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);

  /* Focus Ring */
  --color-focus: #0066FF;

  /* Transitions */
  --transition-fast: 150ms ease;
  --transition-base: 200ms ease;
  --transition-slow: 300ms ease;
}

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

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

モバイルファーストのブレークポイント
/* Mobile-first approach */
.container {
  padding: var(--space-4);

  /* Tablet: 768px and up */
  @media (min-width: 48rem) {
    padding: var(--space-6);
  }

  /* Desktop: 1024px and up */
  @media (min-width: 64rem) {
    padding: var(--space-8);
    max-width: 1200px;
    margin: 0 auto;
  }
}
流動的なタイポグラフィ
/* Responsive typography */
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);
}
グリッドレイアウト
/* Responsive grid */
.grid {
  display: grid;
  gap: var(--space-4);

  /* Auto-fit columns (min 280px) */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

/* 12-column grid system */
.grid-12 {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--space-4);
}

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

/* Stack on mobile */
@media (max-width: 48rem) {
  .col-span-4 {
    grid-column: span 12;
  }
}

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

スキップリンク
export const SkipLink = () => (
  <a href="#main-content" className="skip-link">
    Skip to main content
  </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;
  }
}
フォーカス管理
// Modal with focus trap
export const Modal = ({ isOpen, onClose, children }) => {
  const modalRef = useRef(null);

  useEffect(() => {
    if (isOpen) {
      // Save currently focused element
      const previouslyFocused = document.activeElement;

      // Focus first focusable element in modal
      const firstFocusable = modalRef.current?.querySelector(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
      );
      firstFocusable?.focus();

      // Restore focus on close
      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 ラベル
// Icon button with accessible label
export const IconButton = ({ icon, label, ...props }) => (
  <button
    aria-label={label}
    className="icon-button"
    {...props}
  >
    <span aria-hidden="true">{icon}</span>
  </button>
);

// Loading state
export const LoadingButton = ({ loading, children, ...props }) => (
  <button
    aria-busy={loading}
    aria-live="polite"
    disabled={loading}
    {...props}
  >
    {loading ? 'Loading...' : children}
  </button>
);

パフォーマンス最適化

クリティカル CSS

<style>
  /* Above-the-fold styles */
  body { margin: 0; font-family: var(--font-sans); }
  .header { /* critical header styles */ }
  .hero { /* critical hero styles */ }
</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 lazy loading
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;

  /* Switch to row layout when container > 400px */
  @container (min-width: 400px) {
    flex-direction: row;
  }
}
CSS Grid の Auto-Fit
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: var(--space-4);
}
テーマ設定用のカスタムプロパティ
/* Light theme (default) */
:root {
  --bg: #ffffff;
  --text: #000000;
}

/* Dark theme */
[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 ラベルを設定する
  • ライトモードとダークモードの両方をサポートする
  • タッチターゲットを最低 44x44px にする
  • 見出しの階層を適切に設定する
  • 読み込み状態とエラー状態に対応する

❌ 避けるべきこと:

  • ボタンに div を使用する
  • フォーカススタイルを忘れる
  • 色をハードコードする
  • モバイルのビューポートを無視する
  • 画像の alt テキストを省略する
  • アクセシブルでないモーダルを作成する
CSS のベストプラクティス

✅ 推奨:

  • デザイントークンを使用する
  • モバイルファーストでレスポンシブにする
  • 命名には BEM または CSS モジュールを使用する
  • 論理プロパティを使用する(left ではなく inline-start)
  • モダンなレイアウト(flexbox、grid)を使用する

❌ 避けるべきこと:

  • !important を使用する
  • 深くネストする(3 階層を超える)
  • あらゆる箇所で固定のピクセル値を使う
  • ブラウザー固有のハックを使う
  • インラインスタイルを使う
パフォーマンス

✅ 推奨事項:

  • 画像を遅延読み込みする
  • ルートをコード分割する
  • CSS を最小限にする
  • システムフォントを使用する
  • 画像を最適化する(WebP)
  • クリティカル CSS をインライン化する

❌ 避けるべき事項:

  • 未使用の CSS を読み込む
  • 大きな JavaScript バンドル
  • 最適化されていない画像
  • 読み込みをブロックするリソース
  • 多すぎるウェブフォント

フレームワーク固有のパターン

React
// Composition pattern
export const Card = ({ children }) => (
  <div className="card">{children}</div>
);

export const CardHeader = ({ children }) => (
  <div className="card-header">{children}</div>
);

// Usage
<Card>
  <CardHeader>Title</CardHeader>
  <CardBody>Content</CardBody>
</Card>
Vue

<template>
  <div :class="cardClasses">
    <slot />
  </div>
</template>

<script setup>
const props = defineProps({
  variant: String,
  padding: String
});

const cardClasses = computed(() => [
  'card',
  `card--${props.variant}`,
  `card--padding-${props.padding}`
]);
</script>

リソース

すべての参考資料が含まれています:

  • デザイントークンシステム
  • アクセシビリティチェックリスト(WCAG 2.1)
  • レスポンシブデザインのパターン
  • コンポーネントライブラリのテンプレート
  • パフォーマンス最適化ガイド

まとめ

このスキルで、次のことが可能になります:

  • アクセシブルなコンポーネント - デフォルトで WCAG AA に準拠
  • レスポンシブデザイン - モバイルファーストのアプローチ
  • デザインシステム - トークンベースの一貫性
  • モダン CSS - Flexbox、Grid、カスタムプロパティ
  • パフォーマンス - 最適化された配信
  • ベストプラクティス - 本番環境ですぐに使えるパターン

このスキルを使って、美しく、アクセシブルで、高パフォーマンスなフロントエンドを構築しましょう。


「優れたデザインとは、アクセシブルなデザインです。」

コメント