코드 전 원칙
호버 효과를 전문적으로 보이게 하는 세 가지 규칙:
- 속성을 즉시 바꾸지 말고 항상
transition으로 애니메이션화하세요. 150–250ms 전환은 의도적으로 느껴지고; 0ms는 망가진 느낌입니다. - 움직임에는
transform과opacity를 사용하세요 — GPU 가속됩니다. 레이아웃 재계산을 트리거하는width,height,margin,top애니메이션은 피하세요. prefers-reduced-motion을 존중하세요 — 시스템 전반에서 모션을 비활성화한 사용자는 애니메이션을 원하지 않습니다.
1. 호버 시 리프트 (카드)
.card {
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px -8px rgba(0, 0, 0, 0.25);
}
2. 버튼 채우기 슬라이드
.btn-fill {
position: relative;
overflow: hidden;
color: #7c5cfc;
border: 2px solid #7c5cfc;
padding: 0.75rem 1.5rem;
transition: color 0.3s ease;
z-index: 0;
}
.btn-fill::before {
content: '';
position: absolute; inset: 0;
background: #7c5cfc;
transform: translateX(-100%);
transition: transform 0.3s ease;
z-index: -1;
}
.btn-fill:hover { color: #fff; }
.btn-fill:hover::before { transform: translateX(0); }
3. 밑줄 슬라이드 (링크)
.link-underline {
position: relative;
text-decoration: none;
}
.link-underline::after {
content: '';
position: absolute;
left: 0; bottom: -2px;
width: 100%; height: 2px;
background: currentColor;
transform: scaleX(0);
transform-origin: left;
transition: transform 0.3s ease;
}
.link-underline:hover::after { transform: scaleX(1); }
4. 이미지 줌 (컨테이너 내부)
.img-zoom { overflow: hidden; border-radius: 12px; }
.img-zoom img { transition: transform 0.5s ease; }
.img-zoom:hover img { transform: scale(1.08); }
5. 발광 윤곽선 (접근 가능한 포커스 링)
.btn-glow { transition: box-shadow 0.25s ease; }
.btn-glow:hover, .btn-glow:focus-visible {
box-shadow: 0 0 0 4px rgba(124, 92, 252, 0.3);
outline: none;
}
6. 아이콘 회전
.icon-rotate {
display: inline-block;
transition: transform 0.4s ease;
}
.icon-rotate:hover { transform: rotate(90deg); }
7. 화살표 슬라이드 앞으로
.link-arrow .arrow {
display: inline-block;
transition: transform 0.2s ease;
}
.link-arrow:hover .arrow { transform: translateX(4px); }
8. 그라디언트 시프트
.btn-gradient {
background: linear-gradient(135deg, #7c5cfc, #ec4899, #7c5cfc);
background-size: 200% 200%;
background-position: 0% 50%;
transition: background-position 0.5s ease;
color: #fff;
padding: 0.75rem 1.5rem;
border-radius: 8px;
}
.btn-gradient:hover { background-position: 100% 50%; }
9. 틸트 (3D 원근법)
.tilt {
transform-style: preserve-3d;
perspective: 1000px;
transition: transform 0.3s ease;
}
.tilt:hover {
transform: perspective(1000px) rotateX(4deg) rotateY(-4deg) translateZ(10px);
}
10. 샤인 스윕 (프리미엄)
.shine { position: relative; overflow: hidden; }
.shine::after {
content: '';
position: absolute;
top: 0; left: -100%;
width: 50%; height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transform: skewX(-20deg);
transition: left 0.6s ease;
}
.shine:hover::after { left: 150%; }
11. 테두리 그리기
.border-draw {
position: relative;
padding: 1rem 2rem;
}
.border-draw::before {
content: '';
position: absolute;
inset: 0;
border: 2px solid transparent;
width: 0; height: 100%;
border-top-color: #7c5cfc;
border-left-color: #7c5cfc;
transition: width 0.3s ease, height 0.3s ease 0.3s;
}
.border-draw:hover::before { width: 100%; }
12. 스케일 & 그림자 콤보
.scale-shadow { transition: transform 0.3s, box-shadow 0.3s; }
.scale-shadow:hover {
transform: scale(1.03);
box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.3);
}
13. 캡션 슬라이드 업 (이미지 오버레이)
.caption-slide { position: relative; overflow: hidden; }
.caption-slide .caption {
position: absolute;
left: 0; right: 0; bottom: 0;
padding: 1rem;
background: rgba(0, 0, 0, 0.75);
color: #fff;
transform: translateY(100%);
transition: transform 0.3s ease;
}
.caption-slide:hover .caption { transform: translateY(0); }
14. 형제 요소 채도 감소
.grid .item { transition: opacity 0.3s, filter 0.3s; }
.grid:has(.item:hover) .item:not(:hover) {
opacity: 0.5;
filter: saturate(0.5);
}
15. 리플 확장 (머티리얼 스타일)
.ripple { position: relative; overflow: hidden; }
.ripple::after {
content: '';
position: absolute;
left: 50%; top: 50%;
width: 0; height: 0;
border-radius: 50%;
background: rgba(124, 92, 252, 0.3);
transform: translate(-50%, -50%);
transition: width 0.5s ease, height 0.5s ease;
}
.ripple:hover::after { width: 300%; height: 300%; }
자주 묻는 질문
- 호버 효과가 모바일에서 작동하나요?
- 대부분 작동하지 않습니다 — 모바일에는 호버 상태가 없습니다. 탭이 잠시
:hover를 트리거한 후 탐색하므로 종종 시각적 결함이 발생합니다.@media (hover: hover)를 사용하여 호버 효과를 호버를 지원하는 기기로 범위를 좁히고 모바일 피드백을 위해 일치하는:active상태를 추가하세요. - 이상적인 전환 지속 시간은?
- 대부분의 호버에 150–300ms. 100ms 미만은 즉시처럼 느껴지고(인지된 애니메이션 없음). 400ms 이상은 느릿느릿 — 애니메이션이 끝날 때까지 사용자는 이미 이동했습니다. 샤인이나 리플 같은 장식 효과의 경우 400–600ms가 허용됩니다.
- ease-in-out을 사용해야 하나요, 사용자 정의 곡선?
cubic-bezier(0.4, 0, 0.2, 1)(머티리얼의 표준 ease)이 가장 안전한 기본값 — 빠르게 시작하고 부드럽게 끝납니다.ease-out은 대부분의 경우에 작동합니다.- 상위 요소가 호버될 때 자식 요소에 호버하는 방법은?
- 후손 선택자 사용:
.parent:hover .child { ... }. 마우스가.parent어디에 있든 적용되며, 여기에는.child가 포함됩니다. - 호버 효과가 깜박이거나 떨리는 이유는?
- 두 가지 일반적인 원인: (1) 호버가 요소의 크기에 영향을 주는 속성(예:
border-width)을 변경하여 레이아웃 시프트를 일으키고 커서가 잠시 요소를 떠남; (2)transition이 없어 호버 진입 또는 종료 시 변경이 즉시 발생.