웹 디자인 패턴
프로덕션 수준의 인터페이스를 위한 CSS 구현 패턴입니다. 디자인 기초와 의사 결정에 대해서는 ui-design을 참조하세요. 이 스킬은 코드에 중점을 둡니다.
참고: 타이포그래피/색상/간격 이론은
ui-design, 창의적인 미학은frontend-design을 참조하세요.
설치
OpenClaw / Moltbot / Clawbot
npx clawhub@latest install web-design
레이아웃 원칙
2차원 레이아웃에는 CSS Grid를 사용하고, 1차원 흐름에는 Flexbox를 사용하세요. 각 상황에 맞는 올바른 도구를 선택하세요.
| 레이아웃 요구사항 | 도구 | 이유 |
|---|---|---|
| 페이지 수준 구조 | CSS Grid (grid-template-areas) | 명명된 영역, 명시적인 행/열 제어 |
| 내비게이션 바 | Flexbox | 단일 축 정렬, gap을 사용한 간격 |
| 카드 그리드 | Grid (auto-fill / auto-fit) | 미디어 쿼리 없이 반응형 |
| 중앙 정렬 | Grid (place-items: center) | 가장 짧고 신뢰할 수 있는 중앙 정렬 |
| 사이드바 + 콘텐츠 | Grid (grid-template-columns: 250px 1fr) | 고정 사이드바와 비례적 크기 조정 |
| 겹침 쌓기 | Grid + grid-area: 1/1 | position: absolute 없이 요소 레이어링 |
공간 구성
예측 가능한 레이아웃에서 벗어나세요. 의도적인 비대칭, 겹치는 요소, 그리드를 벗어난 악센트는 시각적 흥미를 유발합니다. 겹치는 요소에는 position: absolute 대신 그리드 쌓기(grid-area: 1/1)를 사용하세요. 고급/편집 미학을 위해서는 넉넉한 여백을, 데이터가 풍부한 인터페이스에는 제어된 밀도를 선택하세요. 선택은 의도적이어야 합니다.
타이포그래피
타이포그래피는 디자인 개성의 90%를 전달합니다. 인터페이스 목적에 맞는 글꼴을 선택하세요.
| 컨텍스트 | 디스플레이 글꼴 방향 | 본문 글꼴 방향 | 예시 페어링 |
|---|---|---|---|
| 편집 / 매거진 | 고대비 세리프 | 중립적인 휴머니스트 산세리프 | Playfair Display + Source Sans 3 |
| SaaS 대시보드 | 기하학적 산세리프 | 일치하는 두께 산세리프 | DM Sans + DM Mono (데이터) |
| 크리에이티브 포트폴리오 | 표현적인 디스플레이 | 깔끔하고 읽기 쉬운 산세리프 | Syne + Outfit |
| 이커머스 럭셔리 | 얇고 현대적인 세리프 | 우아한 산세리프 | Cormorant Garamond + Jost |
| 개발자 도구 | 고정폭 디스플레이 | 고정폭 본문 | JetBrains Mono + IBM Plex Mono |
타입 스케일
일관된 비율을 사용하세요. 1.25(장3도) 스케일은 대부분의 인터페이스에 적합합니다: text-xs 0.64rem, text-sm 0.8rem, text-base 1rem, text-lg 1.25rem, text-xl 1.563rem, text-2xl 1.953rem, text-3xl 2.441rem, text-4xl 3.052rem. 본문 텍스트는 1rem(최소 16px), 줄 높이는 본문 1.5, 제목 1.1–1.2로 설정하세요. 줄 길이는 60–75ch로 제한하세요.
색상
팔레트 구축
모든 팔레트에는 다섯 가지 기능적 역할이 필요합니다:
| 역할 | 목적 | 예시 사용처 |
|---|---|---|
| 기본 | 브랜드 아이덴티티, 기본 동작 | 버튼, 링크, 활성 상태 |
| 중립 | 텍스트, 테두리, 배경 | 본문 텍스트, 카드, 구분선 |
| 악센트 | 보조 동작, 하이라이트 | 태그, 배지, 보조 버튼 |
| 성공 / 경고 / 오류 | 의미론적 피드백 | 토스트, 양식 유효성 검사, 상태 |
| 표면 | 계층화된 배경 | 페이지 위의 카드, 오버레이 위의 모달 |
대비와 깊이
그림자뿐만 아니라 표면 레이어링을 통해 깊이를 만드세요:
:root {
--surface-0: hsl(220 15% 8%); /* 페이지 배경 */
--surface-1: hsl(220 15% 12%); /* 카드 */
--surface-2: hsl(220 15% 16%); /* 올라간 요소 */
--surface-3: hsl(220 15% 20%); /* 팝오버 / 모달 */
}
지각적으로 균일한 색상 조작을 위해 HSL 또는 OKLCH를 사용하세요. 선명한 악센트가 있는 지배적인 색상이 균등하게 분포된 팔레트보다 성능이 뛰어납니다. 항상 WCAG 대비를 확인하세요: 일반 텍스트 4.5:1, 큰 텍스트 3:1.
간격
일관된 간격은 리듬을 만듭니다. 8px 기본 단위(또는 조밀한 UI의 경우 4px)를 사용하세요:
| 토큰 | 값 | 용도 |
|---|---|---|
--space-1 | 0.25rem (4px) | 인라인 아이콘 간격, 좁은 패딩 |
--space-2 | 0.5rem (8px) | 입력 패딩, 컴팩트 목록 |
--space-3 | 0.75rem (12px) | 버튼 패딩, 카드 내부 간격 |
--space-4 | 1rem (16px) | 기본 요소 간격 |
--space-6 | 1.5rem (24px) | 섹션 패딩, 카드 간격 |
--space-8 | 2rem (32px) | 섹션 구분 |
--space-12 | 3rem (48px) | 주요 섹션 구분 |
--space-16 | 4rem (64px) | 페이지 수준 수직 리듬 |
간격을 일관되게 적용하세요: 자식 요소에 여백을 사용하는 대신 Grid/Flexbox 컨테이너에 gap을 사용하세요. 이렇게 하면 여백 축소 버그가 제거되고 반응형 조정이 간소화됩니다.
시각적 계층 구조
크기, 두께, 색상 및 공간의 의도적인 대비를 통해 시선을 유도하세요.
계층 구조 기법
| 기법 | 방법 | 효과 |
|---|---|---|
| 크기 대비 | 히어로 제목 본문 크기의 3~4배 | 즉각적인 초점 |
| 두께 대비 | 굵은 제목 + 일반 본문 | 스캔 가능성 |
| 색상 대비 | 기본 텍스트 vs 흐릿한 보조 텍스트 | 정보 계층화 |
| 공간 그룹화 | 그룹 내 좁은 간격, 그룹 간 넓은 간격 | 게슈탈트 근접성 |
| 입체감 | 그림자 / 표면 레이어 | 상호작용 가능성 |
| 여백 분리 | 주요 요소 주변의 빈 공간 | 부재를 통한 강조 |
실용 패턴 — 카드 계층 구조
카드 내에서 계층 구조를 레이어링하세요: 아이브로우(xs, 대문자, 흐릿) → 제목(xl, 세미볼드) → 본문(기본, 보조 색상, 1.6 줄 높이) → 동작(margin-top으로 간격 띄움). 표면 색상을 사용하여 분리하고 간격 토큰의 일관된 패딩을 사용하세요.
반응형 디자인
중단점 전략
| 중단점 | 대상 | 접근 방식 |
|---|---|---|
< 640px | 모바일 | 단일 열, 쌓인 내비게이션, 터치 대상 ≥ 44px |
640–1024px | 태블릿 | 2열 옵션, 접을 수 있는 사이드바 |
1024–1440px | 데스크톱 | 전체 레이아웃, 호버 상호작용 활성화 |
> 1440px | 와이드 | 최대 너비 컨테이너(1280px), 지나치게 긴 줄 길이 방지 |
유동적 기법
가능하면 엄격한 중단점보다 유동적 크기 조정을 선호하세요:
/* 유동적 타이포그래피 — 640px에서 1440px 뷰포트 사이에서 크기 조정 */
h1 { font-size: clamp(2rem, 1.5rem + 2.5vw, 3.5rem); }
/* 유동적 간격 */
section { padding-block: clamp(2rem, 1rem + 4vw, 6rem); }
/* 본질적 그리드 — 미디어 쿼리 불필요 */
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); gap: var(--space-6); }
컴포넌트의 레이아웃이 뷰포트가 아닌 컨테이너에 반응해야 하는 경우 컴포넌트 수준 반응형을 위해 컨테이너 쿼리(@container)를 사용하세요.
접근성
접근성은 선택 사항이 아닙니다. 처음부터 구축하세요.
| 요구사항 | 구현 | 표준 |
|---|---|---|
| 색상 대비 | 일반 텍스트 4.5:1, 큰 텍스트/UI 3:1 | WCAG 2.1 AA |
| 키보드 내비게이션 | 모든 상호작용 요소에 포커스 가능 및 조작 가능 | WCAG 2.1.1 |
| 포커스 표시기 | 보이는 :focus-visible 링, 2px+ 오프셋 | WCAG 2.4.7 |
| 시맨틱 HTML | <button>, <nav>, <main>, <article> 등 사용 | WCAG 1.3.1 |
| 대체 텍스트 | 정보 제공 이미지에는 설명적, 장식용 이미지에는 alt="" | WCAG 1.1.1 |
| 모션 안전 | prefers-reduced-motion 준수 | WCAG 2.3.3 |
| 터치 대상 | 최소 44×44px 상호작용 영역 | WCAG 2.5.8 |
| 필요한 경우 ARIA | aria-label, aria-live, role은 기본 시맨틱이 불충분할 때만 사용 | WCAG 4.1.2 |
/* 강력한 포커스 표시기 */
:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* 모션 선호도 준수 */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
컴포넌트 디자인
잘 디자인된 컴포넌트의 구성 요소
모든 UI 컴포넌트는 명확한 상태, 일관된 간격 및 예측 가능한 동작을 가져야 합니다:
| 상태 | 시각적 처리 | 예시 |
|---|---|---|
| 기본 | 기본 스타일링 | 정지 상태의 버튼 |
| 호버 | 미묘한 변화 — 배경, 그림자 또는 크기 | background 5-10% 밝게 |
| 활성 / 누름 | 압축된 느낌 — 그림자 감소, 약간 들어감 | transform: scale(0.98) |
| 포커스 | 가시성 높은 링, 윤곽선 제거 안 함 | :focus-visible 링 |
| 비활성화됨 | 불투명도 감소, cursor: not-allowed | opacity: 0.5 |
| 로딩 중 | 스피너 또는 스켈레톤, 상호작용 비활성화 | 레이블을 대체하는 인라인 스피너 |
디자인 토큰 아키텍처
유지보수성을 위해 토큰을 세 가지 레이어로 구조화하세요:
/* 레이어 1: 기본 값 */
--blue-500: oklch(0.55 0.2 250);
--gray-100: oklch(0.95 0.005 250);
--radius-md: 0.5rem;
/* 레이어 2: 의미론적 별칭 */
--color-primary: var(--blue-500);
--color-surface: var(--gray-100);
--radius-button: var(--radius-md);
/* 레이어 3: 컴포넌트별 */
--btn-bg: var(--color-primary);
--btn-radius: var(--radius-button);
--btn-padding: var(--space-2) var(--space-4);
이 3계층 접근 방식은 컴포넌트를 건드리지 않고 레이어 2를 다시 매핑하여 테마 전환을 가능하게 합니다.
인터랙션 패턴
모션과 애니메이션
모션을 장식이 아닌 상태 변화를 전달하는 데 사용하세요. 영향력이 큰 순간에 집중하세요:
| 상호작용 | 지속 시간 | 이징 | 목적 |
|---|---|---|---|
| 버튼 호버 | 150ms | ease-out | 상호작용 인정 |
| 모달 열기 | 250ms | ease-out | 주목 유도 |
| 모달 닫기 | 200ms | ease-in | 빠른 해제 |
| 페이지 전환 | 300ms | ease-in-out | 공간적 맥락 유지 |
| 지연 표시 | 항목당 50–80ms 지연 | ease-out | 순차적 콘텐츠 로딩 |
| 미세 피드백 | 100ms | ease-out | 토글, 체크박스, 스위치 |
/* 지연된 입장 애니메이션 */
.stagger-item {
opacity: 0;
translate: 0 1rem;
animation: reveal 0.5s ease-out forwards;
}
.stagger-item:nth-child(1) { animation-delay: 0ms; }
.stagger-item:nth-child(2) { animation-delay: 60ms; }
.stagger-item:nth-child(3) { animation-delay: 120ms; }
@keyframes reveal {
to { opacity: 1; translate: 0 0; }
}
스크롤 기반 효과
JavaScript 없이 시차 및 표시 효과를 위해 기본 animation-timeline: scroll()(@supports 뒤에 사용)을 사용하세요. 기능 감지로 감싸서 우아하게 저하되도록 하세요.
디자인 품질 체크리스트
배포 전에 다음 기준에 대해 확인하세요:
- 타이포그래피: 의도적인 글꼴 페어링, 일관된 스케일, 읽을 수 있는 줄 길이
- 색상: 응집력 있는 팔레트, WCAG 대비 충족, 의미론적 피드백 색상 정의됨
- 간격: 간격 토큰을 사용한 일관된 리듬, 임시 픽셀 값 없음
- 계층 구조: 명확한 시각적 흐름 — 눈의 경로가 의도된 읽기 순서를 따름
- 반응성: 모바일, 태블릿, 데스크톱에서 테스트됨; 수평 오버플로 없음
- 접근성: 키보드 탐색 가능, 포커스 표시, 스크린 리더 테스트 완료, 모션 안전
- 상태: 모든 상호작용 요소에 호버, 활성, 포커스, 비활성화 및 로딩 상태 있음
- 개성: 디자인이 명확한 관점을 가짐 — 일반 템플릿 미학이 아님
- 성능: 이미지 최적화, 글꼴 서브셋, GPU 가속 애니메이션(
transform,opacity) - 다크 모드: 지원되는 경우 표면은 반전된 색상이 아닌 계층화된 명도를 사용