CSS 텍스트 강조 애니메이션 9종 — 한 줄 복붙으로
CSS 텍스트 강조 애니메이션은 본문 속 한 단어나 문장에 형광펜·밑줄·취소선 같은 시각 효과를 얹어 읽는 사람의 시선을 그 자리에 멈추게 하는 기법입니다. 아홉 가지를 모았고, 전부 자바스크립트 없이 순수 CSS keyframes만으로 자동 반복됩니다.
자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip
아홉 개는 인기 순이 아니라 문장에서 강조가 필요한 상황 순서로 놓았습니다. 문서 속 핵심 문구를 칠하고(01~03), 경고나 완료를 표시하고(04~05), 링크와 안내선으로 이끌고(06~07), 마지막으로 배지와 숫자를 튀게 합니다(08~09). 항목마다 코드는 핵심만 실었고 전체 파일은 zip에 있습니다.
01형광펜 강조
키워드 뒤에 노란 블록을 겹치고 왼쪽부터 차오르게 칠합니다. 약관이나 안내문에서 꼭 봐야 할 문구를 가리킬 때 씁니다.
.hl {
position: relative;
isolation: isolate;
&::before {
content: ""; position: absolute; inset: 12% -2% 6%;
background: $color; border-radius: $r-xs;
transform: scaleX(0); transform-origin: left center;
mix-blend-mode: multiply; z-index: -1;
animation: mark $duration $easing infinite;
}
}
@keyframes mark {
0% { transform: scaleX(0); }
20% { transform: scaleX(1); }
75% { transform: scaleX(1); }
100% { transform: scaleX(0); }
}
02마커 겹침
같은 형광펜 블록을 두 겹 겹치되 서로 다른 각도와 타이밍으로 칠해 손으로 두 번 그은 듯한 두께를 만듭니다. 페이지에서 가장 중요한 한 단어에 씁니다.
.hl {
position: relative;
isolation: isolate;
}
.hl::before, .hl::after {
content: ""; position: absolute; inset: 10% -2% 4%;
transform: scaleX(0); transform-origin: left center;
mix-blend-mode: multiply; z-index: -1;
}
.hl::before { background: $stage-yellow; rotate: -1.2deg; animation: mark $duration $easing infinite; }
.hl::after { background: $color; opacity: .55; rotate: 1deg; animation: mark $duration $easing infinite .3s; }
03손글씨 밑줄
인라인 SVG 곡선 한 줄을 stroke-dashoffset으로 왼쪽부터 그려 손으로 방금 그은 밑줄처럼 보이게 합니다. 인용구나 자기소개의 강조어에 씁니다.
.hl__path {
fill: none; stroke: $color; stroke-width: 3; stroke-linecap: round;
stroke-dasharray: 140; stroke-dashoffset: 140;
animation: draw $duration $easing infinite;
}
@keyframes draw {
0% { stroke-dashoffset: 140; }
35% { stroke-dashoffset: 0; }
80% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -140; }
}
04물결 밑줄
네이티브 물결 밑줄에 text-underline-offset만 애니메이션해 경고 문구가 숨 쉬듯 움직이게 합니다. 필수 입력 표시나 맞춤법 경고에 씁니다.
@property --underline-offset {
syntax: "<length>"; inherits: true; initial-value: 1px;
}
.hl {
text-decoration-line: underline;
text-decoration-style: wavy;
text-decoration-color: $color;
text-underline-offset: var(--underline-offset);
animation: wave $duration linear infinite;
}
@keyframes wave {
0%, 100% { --underline-offset: 0px; }
50% { --underline-offset: 5px; }
}
05취소선 긋기
글자 중간 높이의 가로선을 왼쪽부터 그어 취소선이 눈앞에서 그어지는 순간을 만듭니다. 할인 전 가격이나 완료된 할 일에 씁니다.
.price__strike {
position: absolute; left: -2%; right: -2%; top: 52%; height: 2px;
background: $color;
transform: scaleX(0); transform-origin: left center;
animation: strike $duration $easing infinite;
}
@keyframes strike {
0% { transform: scaleX(0); }
30% { transform: scaleX(1); }
80% { transform: scaleX(1); }
100% { transform: scaleX(0); }
}
06배경 채우기
링크 배경을 background-size만으로 왼쪽부터 채웁니다. background-position을 left로 고정해 채워진 자리가 뒤로 밀리지 않습니다.
.nav {
background: linear-gradient(90deg, $color, $color);
background-repeat: no-repeat;
background-position: left center;
background-size: 0% 100%;
animation: fill $duration $easing infinite;
}
@keyframes fill {
0% { background-size: 0% 100%; }
25% { background-size: 100% 100%; }
75% { background-size: 100% 100%; }
100% { background-size: 0% 100%; }
}
07점선 자라기
점을 반복해서 그린 얇은 막대를 scaleX로 늘려 점선이 왼쪽부터 이어 붙는 것처럼 보이게 합니다. 단계별 안내에서 다음 단계로 이끄는 선에 씁니다.
.hl__dots {
background-image: repeating-linear-gradient(90deg, $color 0 3px, transparent 3px 8px);
transform: scaleX(0); transform-origin: left center;
animation: grow $duration $easing infinite;
}
@keyframes grow {
0% { transform: scaleX(0); }
40% { transform: scaleX(1); }
80% { transform: scaleX(1); }
100% { transform: scaleX(0); }
}
08색 스윕
대각선 그러데이션을 무한히 흘려보내 빛 한 줄이 이음새 없이 배지를 훑고 지나가게 합니다. linear 이징이어야 한 바퀴가 끝나는 지점에서 튀지 않습니다.
.badge::after {
background: linear-gradient(115deg, transparent 30%, $color 48%, transparent 66%);
background-size: 250% 100%;
mix-blend-mode: overlay;
animation: sweep $duration linear infinite;
}
@keyframes sweep {
0% { background-position: 150% 0; }
100% { background-position: -150% 0; }
}
09텍스트 셰도 팝
등록한 커스텀 프로퍼티로 그림자 흐림 반경을 애니메이션해 숫자가 한 번 통통 튀며 진하게 강조됩니다. 최종가나 카운트다운 숫자에 씁니다.
@property --shadow-blur {
syntax: "<length>"; inherits: false; initial-value: 0px;
}
.hl {
filter: drop-shadow(0 0 var(--shadow-blur) $color);
animation: pop $duration $easing infinite;
}
@keyframes pop {
0% { transform: scale(1); --shadow-blur: 0px; }
40% { transform: scale(1.06); --shadow-blur: 10px; }
70% { transform: scale(1); --shadow-blur: 4px; }
100% { transform: scale(1); --shadow-blur: 0px; }
}
어디서 깨지는가 — 함정 둘
아홉 개 중 두 개(01 형광펜 강조, 02 마커 겹침)는 처음 만들었을 때 화면에 전혀 안 보였습니다. z-index: -1로 글자 뒤에 숨긴 형광펜 블록을 부모에 isolation: isolate 없이 두면, 포지션이 없는 조상의 배경 페인트보다도 먼저(=아래로) 그려져 완전히 가려집니다. 강조를 담은 요소에 isolation: isolate 한 줄을 더하자 형광펜이 다시 나타났고, 렌더 측정에서 누적면적 0%였던 값이 0.8% 이상으로 올라갔습니다.
04 물결 밑줄도 비슷한 함정이 있었습니다. text-underline-offset을 1px에서 3px 사이로만 흔들자 프레임 사이 변화가 너무 작아 렌더 측정에서 "거의 안 움직이는 애니메이션"으로 잡혔고, 범위를 0px에서 5px로 넓히자 정상적으로 물결이 보였습니다. 값이 작을수록 은은해 보이지만, 은은함과 안 보임은 한 끗 차이입니다. 두 함정 모두 렌더 측정값을 실제로 찍어 보지 않았다면 "눈에는 그럭저럭 보이는데" 정도로 넘어갔을 문제였습니다. 이 두 번의 수정이 이미 반영된 아홉 개 파일을 담은 압축 파일의 비밀번호는 hx9yh6mr이며, 아래 버튼을 누르면 받을 수 있습니다.
접근성
OS에서 모션 줄이기를 켠 사람에게는 아홉 항목 모두 prefers-reduced-motion: reduce 안에서 각기 다른 결과를 보여 줍니다. 움직임은 끄되 강조 자체는 남기는 게 원칙입니다. 01·02는 형광펜이 칠해진 채로 고정되고, 04는 물결 밑줄이 가운데 값에서 멈추며, 09는 그림자가 흐릿하게 유지된 채로 튐만 사라집니다. 03·05·07처럼 SVG·가로선·점선을 위해 별도 레이어를 추가한 항목은 aria-hidden="true"를 걸어 스크린리더가 장식만 건너뛰고 실제 문장은 그대로 읽게 했습니다. reduced-motion 미디어 쿼리가 어느 브라우저부터 되는지는 MDN 문서에 정리돼 있습니다(참고).
형광펜·밑줄 말고 다른 강조 기법은 CSS 애니메이션 카테고리에서 더 찾을 수 있고, 사이트 운영 방식은 소개 페이지에 적어 두었습니다.
FAQ
형광펜과 밑줄을 한 페이지에 같이 써도 되나요?
가능하지만 한 문단에는 강조 기법 하나만 쓰는 게 좋습니다. 형광펜(01·02)과 물결 밑줄(04)을 같은 문장에 겹치면 무엇이 중요한지 오히려 흐려집니다. 취소선(05)·배지 스윕(08)처럼 맥락이 다른 요소끼리는 한 화면에 같이 있어도 자연스럽습니다.
강조되는 단어는 어떻게 정하나요?
HTML에서 강조할 부분만 span으로 감싸고 나머지는 그대로 둡니다. React 판은 zip의 react 폴더처럼 keyword나 before·after props로 문장을 나눠 넘기면 됩니다. 서버에서 받은 문자열을 그대로 그 props에 넣으면 동적인 키워드도 그대로 작동합니다.
애니메이션이 스크린리더에서도 문제없나요?
본문 글자 자체는 손대지 않고 배경·밑줄·그림자 같은 장식 레이어만 움직이므로 텍스트는 그대로 읽힙니다. 03·05·07처럼 별도 SVG·가로선·점선 레이어를 추가한 항목은 aria-hidden으로 스크린리더가 건너뛰게 했습니다. 움직임 자체가 부담스러운 사용자는 접근성 처리 덕분에 강조는 남고 애니메이션만 꺼집니다.