Communitygithub.com

karero/website-design-system

**모양과 시각적 성능** 레이어를 관리합니다: 모바일 그래픽, 다크 모드, 테마 토큰.

website-design-system란 무엇인가요?

웹사이트 디자인 시스템 스킬은 외관과 시각적 성능에 대한 모든 것을 책임집니다. 모바일 그래픽 최적화, 다크 모드 완벽 지원, 테마 토큰 관리, 테이블 스타일링, 이메일 난독화 등 웹사이트의 시각적 일관성과 접근성을 보장합니다. 이 스킬은 Astro 기반으로 제작되었으며, `new-website` 스킬과 긴밀하게 협력하여 `BRAND.md` 파일과 CSS 토큰 블록을 생성하고 유지 관리합니다. 또한 `image` 스킬을 활용하여 이미지 에셋을 생성하고, `website-qa`를 통해 성능 지표(FCP/LCP)를 검증합니다. 주요 기능으로는 다크 모드의 완벽한 구현(FOUC 방지, WCAG AA 대비율 준수), 반응형 모바일 그래픽(WebP/AVIF 포맷, `srcset`/`sizes` 사용, 레이아웃 시프트 방지), 번역된 텍스트 길이에 대응하는 유연한 레이아웃, 접근성을 고려한 테이블 스타일링(`stack`/`figures`), 스팸 수집기를 피하기 위한 이메일 주소 난독화 등을 제공합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/karero/croftweaver/tree/HEAD/skills/website-design-system

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

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

문서

웹사이트 디자인 시스템

모양과 시각적 성능 레이어를 관리합니다: 모바일 그래픽, 다크 모드, 테마 토큰. BRAND.md (템플릿: new-website 스킬의 templates/brand.md) 와 해당 스킬의 templates/astro/src/styles/global.css 에 있는 토큰 블록을 출력/유지 관리합니다. 이미지 규칙은 아래에 있습니다 (Astro의 astro:assets를 통한 WebP/AVIF); images.spec.ts 테스트가 이를 강제합니다. 이미지 에셋 생성에는 image 스킬을 사용하세요.

다크 모드 (최우선)

  • 하드코딩된 색상이 아닌 토큰. 모든 색상은 CSS 사용자 정의 속성을 통해 흐릅니다; 라이트는 :root 기본값이고, 다크는 :root[data-theme="dark"] 아래에서 재정의되며, JavaScript를 사용하지 않는 방문자를 위한 @media (prefers-color-scheme: dark) 블록도 있습니다. 스타터가 이 정확한 구조를 제공합니다.
  • FOUC 방지 스크립트는 <head>에서 localStorage.theme 또는 시스템 기본 설정을 첫 페인트 전에 data-theme으로 설정합니다 (Base.astro에 이미 연결됨). 토글은 localStorage.theme을 작성합니다 (스타터는 제공하지 않음; 추가하는 경우 개인정보 보호 페이지 섹션 3에 명시).
  • 두 테마 모두 대비: 모든 텍스트/배경 쌍이 WCAG AA (본문 4.5:1, 큰/UI 요소 3:1)를 통과합니다. 라이트에서 통과하는 색상이 다크에서는 종종 실패합니다 - 접근성 테스트는 라이트+다크 전체 조합을 실행하므로 둘 다 확인하세요.

모바일 및 반응형 그래픽

  • 모바일 우선: 360px부터 디자인; 탭 대상 ≥ 44px; 가로 스크롤 금지. 360 / 768 / 1280 (preview_resize)에서 확인하세요.
  • 최신 포맷: 사진의 경우 JPEG/PNG 대신 WebP (또는 AVIF).
  • 디스플레이 크기에 맞게 조정, 원본 크기 사용 금지. 2000px 이미지를 80px 슬롯에 절대 제공하지 마세요; 너비 변형을 생성하고 srcset/sizes (또는 <picture>)를 사용하세요.
  • 레이아웃 시프트 없음: 명시적인 width/height 속성을 설정하여 이미지가 로드되기 전에 박스가 예약되도록 합니다 (CLS / LCP 보호). Astro의 <Image>가 자동으로 설정합니다; images.spec.ts는 해당 속성을 요구합니다 (CSS 전용 aspect-ratio는 테스트를 통과하지 못합니다).
  • 지연 vs 즉시 로딩: loading="lazy"는 접힌 부분 아래에; LCP/히어로 이미지는 fetchpriority="high"로 즉시 로드합니다 (지연 로드하지 마세요).
  • 모든 이미지에 대체 텍스트 (순전히 장식용인 경우에만 alt="").
  • 폰트: font-display: swap과 함께 자체 호스팅 woff2 사용; 접힌 부분 위에서 사용되는 1–2개만 미리 로드합니다. 늦은 폰트 교체로 인한 레이아웃 시프트를 피하세요.

텍스트 길이 대응 (번역된 사본)

  • 번역된 텍스트는 더 길어집니다. 독일어 (및 기타 여러 언어)는 일반적으로 동등한 영어 사본보다 20–35% 더 깁니다. 영어 플레이스홀더 텍스트에 맞게 크기가 조정된 버튼, 탐색 링크 또는 카드는 실제 번역된 사본으로 대체되는 순간 자동으로 오버플로되거나, 보기 흉하게 줄 바꿈되거나, 잘립니다.
  • 유연한 너비 선호. 번역된 텍스트를 포함하는 요소에는 고정 width (영어 문자열에 맞춰진) 대신 min-width / flex / grid auto/fr 크기 조정을 사용하세요.
  • 오버플로 ≠ 너비: 유연한 크기 조정이 단일 긴 합성 명사 (Datenschutzerklärung)가 좁은 열을 터뜨리는 것을 막지는 못합니다 — hyphens: auto (Base.astro가 이미 설정한 lang 속성을 통해 언어별로 올바름) 와 overflow-wrap: break-word 대비책이 필요합니다; 스타터의 global.css는 body에 둘 다 제공합니다. 표 셀은 하이픈을 끕니다 (표 참조).
  • white-space: nowrap + text-overflow: ellipsis에만 의존하지 마세요 탐색 링크나 버튼 레이블 처리를 위해 — 탐색 링크를 자르는 것은 일반적으로 허용 가능한 UX가 아닙니다 (방문자가 어디로 가는지 알 수 없게 됩니다). 잘림이 허용된 절충안인 콘텐츠(예: 표 셀)에는 괜찮지만, 탐색에는 적합하지 않습니다.
  • 영어 플레이스홀더가 아닌 실제 번역된 레이블로 확인하세요. 사이트가 astro-i18n-setup의 LanguageSwitcher.astro 또는 모든 이중/다국어 탐색을 제공할 때, 영어 플레이스홀더뿐만 아니라 모든 언어의 실제 제공된 사본을 사용하여 360/768/1280 (preview_resize)에서 레이아웃을 확인하세요.

표

모든 표에 대해 하나의 스타일; 페이지는 표에 대한 레이아웃 CSS를 전혀 포함하지 않습니다. 스타터의 src/styles/tables.css (Base.astro에 의해 global.css 다음에 임포트됨) 에 있으며 tests/tables.spec.ts가 이를 확인합니다. 이 섹션 이전에 만들어진 사이트는 tables.css를 복사하고, 레이아웃에 임포트 줄을 추가하고, 먼저 각 페이지의 자체 표 CSS를 제거합니다.

셀이 무엇을 담고 있는지에 따라 두 가지 종류로 구분됩니다:

  • stack (문장): 40rem 이상의 표; 그 아래에서는 각 행이 쌓이고, 행 헤더는 제목으로, 각 셀은 해당 열 이름 아래에 표시됩니다. 4개의 산문 열은 320px를 공유할 수 없습니다.
  • figures (짧은 숫자, 숫자 셀에 class="num"): 휴대폰에서도 표 형태를 유지하며 320px에 맞아야 합니다. 더 이상 맞지 않으면 stack으로 만드세요; 스크롤 막대는 해결책이 아닙니다. stack figures가 함께 쌓인 레이아웃에서의 수치 모양입니다.

모든 표의 마크업:

<div class="table-wrap stack" role="region" aria-label="표가 보여주는 내용" tabindex="0">
  <table role="table">
    <thead role="rowgroup"><tr role="row">
      <th scope="col" role="columnheader">{cols[0]}</th><th scope="col" role="columnheader">{cols[1]}</th>
    </tr></thead>
    <tbody role="rowgroup">{rows.map((r) => (
      <tr role="row"><th scope="row" role="rowheader">{r.item}</th><td role="cell" data-label={cols[1]}>{r.text}</td></tr>
    ))}</tbody>
  </table>
</div>
  • 열 이름을 한 번 목록으로 작성하고, 헤더와 모든 data-label에 사용하세요: 쌓인 셀의 레이블은 해당 열 헤더와 같아야 하며, 테스트가 둘을 비교합니다. figures 표에는 셀별 역할과 data-label이 필요 없습니다.
  • 쌓인 표는 더 이상 display: table이 아닙니다. 역할은 레이아웃이 변경될 때 표를 삭제하는 브라우저를 위한 대비책입니다; 거기서 작동하는지는 확인되지 않습니다. Chromium에서 실제 표를 기준으로 측정했을 때, 접근성 트리는 역할 없이도 표를 유지했습니다.
  • 셀에 단독으로 있는 링크의 class="tap"은 휴대폰에서 44px 탭 대상으로 만듭니다. nowrap은 짧은 값(1, 3, 6)을 한 줄로 유지합니다. long-head (stack과 함께)는 행 헤더가 문장일 때 첫 번째 열을 넓힙니다.
  • 셀 내의 단어는 하이픈으로 연결되지 않습니다. 본문의 hyphens: auto는 좁은 첫 번째 열을 조각("An-thropic")으로 압축하므로, 블록은 거기에 hyphens: manual을 설정합니다.
  • 외부에서 가져와 마크업을 수행할 수 없는 표가 있는 페이지는 TABLE_EXEMPT (이유 포함)에 들어갑니다. PAGES는 그대로 유지됩니다: 사이트맵과 llms.txt 검사는 모든 활성 경로가 필요합니다.

tables.spec.ts가 확인하는 내용: 모든 표는 해당 종류의 .table-wrap 안에 있으며, region, label 및 tabindex, 모든 헤더 셀에 scope="col"이 있고 각 행의 첫 번째 셀로 <th scope="row">가 있으며 나머지는 <td>입니다; 쌓인 표는 역할과 각 셀의 헤더와 동일한 레이블을 가지고 있습니다; 360 및 320px에서 어떤 페이지나 표도 옆으로 스크롤되지 않습니다; 641px 이상에서 쌓인 표는 다시 표가 되어 헤더 행이 표시되고 단어가 끊어지지 않습니다 (이는 overflow-wrap: anywhere와 같이 열이 단어를 압축하도록 하는 규칙을 잡아냅니다); 어떤 셀에도 hyphens: auto가 없습니다. 또한 홈 페이지에 추가하는 fixture에서 CSS를 테스트하고, 두 테마에서 axe를 사용하므로 표가 없는 사이트도 블록을 증명합니다. Headless Chromium은 하이픈을 넣지 않으므로 하이픈 가드는 계산된 스타일을 읽고 레이아웃을 읽지 않습니다. Safari 및 VoiceOver는 확인되지 않습니다.

이메일 난독화 (크롤러로부터 주소 숨기기)

일반 텍스트 이메일 또는 mailto:your@addr를 HTML 소스에 절대 넣지 마세요 — 스크레이퍼가 스팸을 위해 몇 시간 내에 수집합니다. 모든 주소는 EmailLink.astro 컴포넌트 (new-website 스킬의 templates/astro/에 있는 src/components/EmailLink.astro, + 해당 lib/obfuscate.ts 헬퍼)를 통해 렌더링하세요: 주소는 빌드 시 역순 + base64 인코딩되어 브라우저에서 다시 조립되므로, 제공된 HTML에는 읽을 수 있는 주소가 포함되지 않습니다. JavaScript를 사용하지 않는 방문자는 여전히 사용 가능한 "name [at] domain [dot] com" 힌트를 볼 수 있습니다.

<EmailLink address="[email protected]" subject="프로젝트 문의" text="이메일 보내기" />

규칙: 빌드된 HTML에 원시 mailto: 링크 및 리터럴 @ 주소가 0개 — website-qa가 이를 확인합니다. 수집을 원하지 않는 전화번호에도 동일한 아이디어가 적용됩니다.

브랜드 토큰

BRAND.md의 팔레트를 토큰 블록에 미러링하고 동기화 상태를 유지하세요. 정의: 브랜드(고정), 테마별 heading/ink/muted/accent/link/line/bg/bg-soft/surface. accent와 link 색상은 두 테마 모두에서 배경에 대해 AA를 통과해야 하며, 이것이 각 테마가 자체를 가지는 이유입니다. 선택적 website-brand-kit은 검사와 함께 미러링을 수행합니다: 입력은 BRAND.md의 표를 읽고, 모든 텍스트 색상을 두 테마의 모든 배경에 대해 4.5:1로 유지하고, 값을 global.css의 세 가지 색상 블록(light, dark, JavaScript 없는 방문자를 위한 dark 블록) 모두에 씁니다. 수동으로 세 블록을 모두 함께 변경하세요.

완료 조건

BRAND.md가 채워져 있고 토큰 블록과 일치함; 이미지는 WebP이고, 크기가 조정되었으며, lazy/eager가 올바르고, alt가 있음; 접근성 테스트(light+dark)가 통과됨; 레이아웃이 360/768/1280에서 시프트 없이 유지됨; 모든 표가 stack 또는 figures이고 tables.spec.ts가 통과됨. 성능 수치(FCP/LCP)는 website-qa에서 확인됨. 레이아웃은 기본 로케일뿐만 아니라 모든 제공 언어의 실제 (플레이스홀더가 아닌) 사본으로 360/768/1280에서도 유지됨.

Individual skills in this repo

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

관련 스킬