Communitygithub.com

Samueladewole/claude-skills

접근성, 반응형, 성능을 갖춘 프론트엔드 컴포넌트를 디자인 시스템 모범 사례, 최신 CSS, 프레임워크에 구애받지 않는 패턴으로 구축합니다.

claude-skills란 무엇인가요?

이 스킬은 프론트엔드 디자이너와 개발자가 접근성, 반응형 디자인, 성능 최적화를 최우선으로 하는 고품질 사용자 인터페이스를 구축할 수 있도록 지원합니다. WCAG 2.1 AA 표준을 준수하는 접근성 우선 접근 방식, 모바일 우선 반응형 디자인, 컴포넌트 기반 아키텍처, 디자인 시스템 정렬이라는 5가지 핵심 원칙을 기반으로 합니다. React, Vue, Svelte 등 다양한 프레임워크에서 재사용 가능하고 일관된 컴포넌트를 만들 수 있도록 실제 코드 예제(버튼, 카드, 폼 입력), 디자인 토큰(CSS 사용자 정의 속성), 반응형 디자인 패턴(유동 타이포그래피, 그리드 레이아웃), 접근성 패턴(스킵 링크, 포커스 관리, ARIA 라벨), 성능 최적화 기법(중요 CSS, 지연 로딩, 코드 분할)을 제공합니다. 또한 컨테이너 쿼리, CSS 그리드 auto-fit, 사용자 정의 속성을 이용한 테마 지정 등 최신 CSS 기술을 활용하여 유지 관리가 쉽고 확장 가능한 프론트엔드를 구축하는 방법을 설명합니다. CLI 스크립트를 통해 컴포넌트 생성, 디자인 시스템 설정, 접근성 감사 기능을 제공하여 개발 워크플로우를 자동화합니다.

지원 대상✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/Samueladewole/claude-skills/tree/HEAD/frontend-designer

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

프론트엔드 디자이너 (Frontend Designer)

프론트엔드 디자이너와 개발자가 최신 모범 사례로 아름답고 접근 가능하며 성능이 뛰어난 사용자 인터페이스를 구축할 수 있는 종합적인 스킬입니다.

이 스킬이 하는 일

프론트엔드 디자이너/개발자가 다음을 수행하도록 돕습니다:

  • 컴포넌트 디자인 및 개발 - 재사용 가능하고 접근성 있는 컴포넌트 구축
  • 디자인 시스템 - 토큰, 패턴, 문서화 구현
  • 반응형 디자인 - 모바일 우선, 유동적인 레이아웃
  • 접근성 (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. 디자인 시스템 정렬

  • 디자인 토큰
  • 일관된 간격
  • 타이포그래피 스케일
  • 색상 팔레트
  • 컴포넌트 라이브러리

컴포넌트 패턴

버튼 컴포넌트

접근성 있고 유연한 버튼 패턴:

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

// 로딩 상태
export const 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 그리드 자동 맞춤

.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.

관련 스킬