Communitygithub.com

Blaise1030/astro-marketing

웹 접근성을 감사하고 WCAG 2.2 가이드라인에 따라 개선합니다. '접근성 개선', 'a11y 감사', 'WCAG 준수', '스크린 리더 지원', '키보드 탐색', '접근성 높이기'와 같은 요청이 있을 때 사용하세요.

astro-marketing란 무엇인가요?

이 스킬은 **WCAG 2.2** 및 **Lighthouse 접근성 감사**를 기반으로 포괄적인 접근성 가이드라인을 제공하는 AI 에이전트 스킬입니다. 주된 목표는 장애를 가진 사람을 포함한 모든 사람이 웹 콘텐츠를 사용할 수 있도록 만드는 것입니다. 이 스킬은 증거 기반 감사 워크플로우를 갖추고 있어, Chrome DevTools MCP를 사용한 실시간 Lighthouse 감사, 접근성 트리 스냅샷 검사, 키보드 탐색 등을 통해 문제를 진단하고 수정합니다. 특히 자동화 도구로는 감지할 수 없는 접근성 장벽이 많다는 점을 강조하며, 100점 감사 점수가 WCAG 준수를 의미하지 않음을 명확히 합니다. WCAG의 4대 원칙(POUR: 지각 가능, 운용 가능, 이해 가능, 견고함)을 기반으로 A, AA, AAA 준수 수준을 목표로 하며, 각 수준별로 필요한 패턴과 코드 예제를 제공합니다. 이 스킬은 주로 Claude Code, Cursor, Codex와 같은 AI 코딩 도구에서 사용되도록 설계되었습니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Blaise1030/astro-marketing/tree/HEAD/.agents/skills/accessibility

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

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

미리보기

스킬 README에서

astro-marketing preview
astro-marketing preview
astro-marketing preview

문서

접근성 (a11y)

WCAG 2.2 및 Lighthouse 접근성 감사에 기반한 포괄적인 접근성 가이드라인입니다. 목표: 장애를 가진 사람을 포함한 모든 사람이 콘텐츠를 사용할 수 있도록 합니다.

증거 기반 감사 워크플로우

렌더링된 페이지가 있을 때:

  1. 해당 기능을 사용할 수 있을 때 실시간 Lighthouse 접근성 감사를 실행합니다. Chrome DevTools MCP에서는 lighthouse_audit을 사용합니다. 일반 공개 페이지에는 모바일 탐색 모드를, 리로드 시 인증 또는 사용자 생성 상태가 손실되는 경우에는 스냅샷 모드를 사용합니다.
  2. 실패한 감사 노드를 사용하여 전체 리포지토리에서 일반적인 패턴을 검색하는 대신 관련 컴포넌트 또는 템플릿의 위치를 파악합니다.
  3. 이름, 역할, 상태, 랜드마크 및 제목 구조에 대해 렌더링된 접근성 트리 스냅샷을 검사합니다. Chrome DevTools MCP에서는 take_snapshot을 사용합니다. 키보드로 영향을 받는 흐름을 직접 테스트합니다.
  4. 소스를 수정한 후 동일한 감사 및 수동 상호작용을 다시 실행합니다.

실시간 도구를 사용할 수 없는 경우, Lighthouse CLI 또는 axe를 사용하여 자동화된 적용 범위를 확보하고 동일한 수동 점검을 완료합니다. 자동화 도구는 접근성 장벽의 일부만 감지할 수 있습니다. 100점은 WCAG 준수를 의미하지 않으며, 낮은 점수가 문제 수준의 증거를 대체하지 않습니다.

WCAG 원칙: POUR

원칙설명
Perceivable (지각 가능)콘텐츠가 다른 감각을 통해 인지될 수 있음
Operable (운용 가능)인터페이스가 모든 사용자에 의해 작동될 수 있음
Understandable (이해 가능)콘텐츠와 인터페이스가 이해 가능해야 함
Robust (견고함)콘텐츠가 보조 기술과 함께 작동해야 함

준수 수준

수준요구사항목표
A최소 접근성통과해야 함
AA표준 준수통과해야 함 (많은 관할권에서 법적 요구사항)
AAA향상된 접근성있으면 좋음

지각 가능

대체 텍스트 (1.1)

이미지에는 대체 텍스트가 필요합니다:

<!-- ❌ 대체 텍스트 누락 -->
<img src="chart.png">

<!-- ✅ 설명적인 대체 텍스트 -->
<img src="chart.png" alt="3분기 매출이 40% 증가했음을 보여주는 막대 차트">

<!-- ✅ 장식용 이미지 (빈 대체 텍스트) -->
<img src="decorative-border.png" alt="" role="presentation">

<!-- ✅ 더 긴 설명이 필요한 복잡한 이미지 -->
<figure>
  <img src="infographic.png" alt="2024 시장 동향 인포그래픽" 
       aria-describedby="infographic-desc">
  <figcaption id="infographic-desc">
    <!-- 상세 설명 -->
  </figcaption>
</figure>

아이콘 버튼에는 접근 가능한 이름이 필요합니다:

<!-- ❌ 접근 가능한 이름 없음 -->
<button><svg><!-- 메뉴 아이콘 --></svg></button>

<!-- ✅ aria-label 사용 -->
<button aria-label="메뉴 열기">
  <svg aria-hidden="true"><!-- 메뉴 아이콘 --></svg>
</button>

<!-- ✅ 시각적으로 숨겨진 텍스트 사용 -->
<button>
  <svg aria-hidden="true"><!-- 메뉴 아이콘 --></svg>
  <span class="visually-hidden">메뉴 열기</span>
</button>

시각적으로 숨겨진 클래스:

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

색상 대비 (1.4.3, 1.4.6)

텍스트 크기AA 최소AAA 향상
일반 텍스트 (< 18px / < 14px bold)4.5:17:1
큰 텍스트 (≥ 18px / ≥ 14px bold)3:14.5:1
UI 컴포넌트 및 그래픽3:13:1
/* ❌ 낮은 대비 (2.5:1) */
.low-contrast {
  color: #999;
  background: #fff;
}

/* ✅ 충분한 대비 (7:1) */
.high-contrast {
  color: #333;
  background: #fff;
}

/* ✅ 초점 상태도 대비가 필요합니다 (배경 대비 3:1, WCAG 1.4.11) */
:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

색상에만 의존하지 마세요:

<!-- ❌ 색상만 오류를 나타냄 -->
<input class="error-border">
<style>.error-border { border-color: red; }</style>

<!-- ✅ 색상 + 아이콘 + 텍스트 -->
<div class="field-error">
  <input aria-invalid="true" aria-describedby="email-error">
  <span id="email-error" class="error-message">
    <svg aria-hidden="true"><!-- 오류 아이콘 --></svg>
    유효한 이메일 주소를 입력해주세요
  </span>
</div>

미디어 대체 수단 (1.2)

<!-- 자막이 있는 비디오 -->
<video controls>
  <source src="video.mp4" type="video/mp4">
  <track kind="captions" src="captions.vtt" srclang="en" label="English" default>
  <track kind="descriptions" src="descriptions.vtt" srclang="en" label="Descriptions">
</video>

<!-- 대본이 있는 오디오 -->
<audio controls>
  <source src="podcast.mp3" type="audio/mp3">
</audio>
<details>
  <summary>대본</summary>
  <p>전체 대본 텍스트...</p>
</details>

운용 가능

키보드 접근성 (2.1)

모든 기능은 키보드로 접근 가능해야 합니다. <button>, <a href>, 양식 컨트롤 같은 기본 대화형 요소를 선호하세요. 이러한 요소는 Enter/Space 활성화, 초점, 보조 기술 시맨틱을 무료로 처리합니다. 기본 요소를 사용할 수 없는 경우에만 수동 키보드 처리를 추가하세요.

<!-- ❌ 클릭만 가능한 비대화형 요소: 초점 불가능, 키보드 활성화 없음 -->
<div class="card" onclick="handleAction()">열기</div>

<!-- ✅ 가장 좋음: 기본 버튼 사용 -->
<button type="button" onclick="handleAction()">열기</button>
// ✅ 비대화형 요소(예: role="button"을 가진 div)를 반드시 사용해야 하는 경우,
// 초점 가능하게 만들고 키보드 활성화를 처리하십시오. 기본 <button>에는 이것을 추가하지 마십시오.
// Enter/Space가 이미 클릭을 발생시키므로 두 번 트리거됩니다.
element.setAttribute('role', 'button');
element.setAttribute('tabindex', '0');
element.addEventListener('click', handleAction);
element.addEventListener('keydown', (e) => {
  if (e.key === 'Enter' || e.key === ' ') {
    e.preventDefault();
    handleAction();
  }
});

키보드 트랩이 없어야 합니다. 사용자는 모든 컴포넌트에서 Tab을 이용해 들어오고 나갈 수 있어야 합니다. 대화상자에는 모달 초점 트랩 패턴을 사용하세요. 기본 <dialog> 요소는 이를 자동으로 처리합니다.

초점 가시성 (2.4.7)

/* ❌ 절대 초점 아웃라인을 제거하지 마세요 */
*:focus { outline: none; }

/* ✅ 키보드 전용 초점에는 :focus-visible 사용 */
:focus {
  outline: none;
}

:focus-visible {
  outline: 2px solid currentColor; /* 텍스트 색상 상속 -> 이미 대비 확인됨 */
  outline-offset: 2px;
}

/* ✅ 또는 브랜드 색상을 선택하고 모든 배경에 대해 ≥3:1 대비를 확인 */
button:focus-visible {
  box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);
}

초점이 가려지지 않음 (2.4.11) — 2.2에서 신규

요소가 키보드 초점을 받을 때, 고정 헤더, 푸터, 겹치는 패널과 같은 작성자가 만든 다른 콘텐츠에 의해 완전히 가려져서는 안 됩니다. 레벨 AAA(2.4.12)에서는 초점이 있는 요소의 어떤 부분도 숨겨져서는 안 됩니다.

/* ✅ 초점이 있는 요소로 스크롤할 때 고정 헤더를 고려 */
:target {
  scroll-margin-top: 80px;
}

/* ✅ 초점이 있는 항목이 고정/스티키 막대를 비우도록 함 */
:focus {
  scroll-margin-top: 80px;
  scroll-margin-bottom: 60px;
}

건너뛰기 링크 (2.4.1)

키보드 사용자가 반복되는 탐색을 건너뛸 수 있도록 건너뛰기 링크를 제공하세요. 전체 마크업과 스타일은 건너뛰기 링크 패턴을 참조하세요.

대상 크기 (2.5.8) — 2.2에서 신규

대화형 대상은 최소 24 × 24 CSS 픽셀(AA)이어야 합니다. 예외: 인라인 텍스트 링크, 브라우저가 크기를 제어하는 요소, 경계 상자의 중심에 있는 24px 원이 다른 대상과 겹치지 않는 대상.

/* ✅ 최소 대상 크기 */
button,
[role="button"],
input[type="checkbox"] + label,
input[type="radio"] + label {
  min-width: 24px;
  min-height: 24px;
}

/* ✅ 편안한 대상 크기 (권장 44×44) */
.touch-target {
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

드래그 동작 (2.5.7) — 2.2에서 신규

드래그가 필요한 모든 작업에는 단일 포인터 대안(예: 버튼, 입력)이 있어야 합니다. 정렬 가능한 목록 예제는 드래그 동작 패턴을 참조하세요.

시간 제한 (2.2)

// 사용자가 시간 제한을 연장할 수 있도록 허용
function showSessionWarning() {
  const modal = createModal({
    title: '세션 만료 예정',
    content: '2분 후에 세션이 만료됩니다.',
    actions: [
      { label: '세션 연장', action: extendSession },
      { label: '로그아웃', action: logout }
    ],
    timeout: 120000
  });
}

동작 (2.3)

/* 축소된 동작 선호 설정 존중 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

이해 가능

페이지 언어 (3.1.1)

<!-- ❌ 언어 지정되지 않음 -->
<html>

<!-- ✅ 언어 지정됨 -->
<html lang="ko">

<!-- ✅ 페이지 내에서 언어 변경 -->
<p>프랑스어로 인사말은 <span lang="fr">bonjour</span>입니다.</p>

일관된 탐색 (3.2.3)

<!-- 탐색은 페이지 전체에서 일관되어야 함 -->
<nav aria-label="메인">
  <ul>
    <li><a href="/" aria-current="page">홈</a></li>
    <li><a href="/products">제품</a></li>
    <li><a href="/about">소개</a></li>
  </ul>
</nav>

일관된 도움말 (3.2.6) — 2.2에서 신규

도움말 메커니즘(연락처 정보, 채팅 위젯, FAQ 링크, 자가 도움말 옵션)이 여러 페이지에서 반복되는 경우, 매번 동일한 상대적 순서로 나타나야 합니다. 일관된 배치에 의존하는 사용자가 매 페이지마다 도움말을 찾느라 헤매지 않도록 합니다.

양식 레이블 (3.3.2)

모든 입력에는 프로그래밍 방식으로 연결된 레이블이 필요합니다. 명시적, 암시적 및 지침 예제는 양식 레이블 패턴을 참조하세요.

오류 처리 (3.3.1, 3.3.3)

role="alert" 또는 aria-live를 사용하여 스크린 리더에 오류를 알리고, 잘못된 필드에 aria-invalid="true"를 설정하며, 제출 시 첫 번째 오류에 초점을 맞춥니다. 전체 마크업과 JS는 오류 처리 패턴을 참조하세요.

중복 입력 방지 (3.3.7) — 2.2에서 신규

사용자가 동일한 세션에서 이미 제공한 정보를 다시 입력하도록 강요하지 마세요. 이전 단계에서 자동으로 채우거나, 이전에 입력한 값에서 선택할 수 있도록 하세요. 예외: 보안 재확인 및 만료된 콘텐츠.

<!-- ✅ 청구지 주소를 배송지 주소로 자동 채움 -->
<fieldset>
  <legend>배송지 주소</legend>
  <label>
    <input type="checkbox" id="same-as-billing" checked>
    청구지 주소와 동일
  </label>
  <!-- 체크되면 필드가 자동으로 채워짐 -->
</fieldset>

접근 가능한 인증 (3.3.8) — 2.2에서 신규

로그인 흐름은 인지 기능 테스트(예: 비밀번호 기억, 퍼즐 풀기)에 의존해서는 안 됩니다. 단, 다음 중 하나 이상이 있는 경우는 예외입니다:

  • 복사-붙여넣기 또는 자동 채우기 메커니즘 사용 가능
  • 대체 방법 존재 (예: 패스키, SSO, 이메일 링크)
  • 테스트가 객체 인식 또는 개인 콘텐츠를 사용함 (AA만 해당; AAA는 이 예외를 제거)
<!-- ✅ 비밀번호 필드에 붙여넣기 허용 -->
<input type="password" id="password" autocomplete="current-password">

<!-- ✅ 비밀번호 없는 대안 제공 -->
<button type="button">패스키로 로그인</button>
<button type="button">로그인 링크를 이메일로 보내기</button>

견고함

ARIA 사용법 (4.1.2)

기본 요소 선호:

<!-- ❌ div에 ARIA 역할 사용 -->
<div role="button" tabindex="0">클릭하세요</div>

<!-- ✅ 기본 버튼 -->
<button>클릭하세요</button>

<!-- ❌ ARIA 체크박스 -->
<div role="checkbox" aria-checked="false">옵션</div>

<!-- ✅ 기본 체크박스 -->
<label><input type="checkbox"> 옵션</label>

ARIA가 필요한 경우, 올바른 역할과 상태를 사용하세요. 전체 탭 목록 예제는 ARIA 탭 패턴을 참조하세요.

라이브 리전 (4.1.3)

초점을 이동하지 않고 동적 콘텐츠 변경 사항을 알리려면 aria-live 리전을 사용하세요. 마크업과 showNotification() 헬퍼는 라이브 리전 패턴을 참조하세요.


테스트 체크리스트

자동화 테스트

에이전트에 실패한 렌더링된 노드를 직접 반환하는 실시간 Lighthouse 감사를 선호하세요. Chrome DevTools MCP에서는 lighthouse_audit입니다. 그렇지 않으면:

# Lighthouse 접근성 감사
npx lighthouse https://example.com --only-categories=accessibility

# axe-core
npm install @axe-core/cli -g
axe https://example.com

수동 테스트

  • 키보드 탐색: Tab으로 전체 페이지를 순회하고 Enter/Space로 활성화
  • 스크린 리더: VoiceOver(Mac), NVDA(Windows) 또는 TalkBack(Android)으로 테스트
  • 확대: 200% 확대에서 콘텐츠 사용 가능
  • 고대비: Windows 고대비 모드로 테스트
  • 축소된 동작: prefers-reduced-motion: reduce로 테스트
  • 초점 순서: 논리적이고 시각적 순서를 따름
  • 대상 크기: 대화형 요소가 24×24px 최소 기준 충족

VoiceOver 및 NVDA 단축키는 스크린 리더 명령 참조를 참조하세요.


영향별 일반적인 문제

심각 (즉시 수정)

  1. 양식 레이블 누락
  2. 이미지 대체 텍스트 누락
  3. 불충분한 색상 대비
  4. 키보드 트랩
  5. 초점 표시기 없음

중요 (출시 전 수정)

  1. 페이지 언어 누락
  2. 제목 구조 누락
  3. 설명적이지 않은 링크 텍스트
  4. 자동 재생 미디어
  5. 건너뛰기 링크 누락

보통 (조만간 수정)

  1. 아이콘에 ARIA 레이블 누락
  2. 일관되지 않은 탐색
  3. 오류 식별 누락
  4. 제어 장치 없는 시간 제한
  5. 랜드마크 영역 누락

참고 자료

Individual skills in this repo

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

관련 스킬