CSS 글로우 효과 9종 — 네온사인처럼 빛나는 버튼과 텍스트
CSS 글로우 효과는 버튼이나 글자 주위에 빛이 번지는 것처럼 보이게 만드는 장식이다. 네온사인처럼 빛나 보이지만 이미지나 동영상이 아니라 코드 몇 줄로 만든다. 한 줄 복붙으로 그대로 옮겨 쓸 수 있는 CSS 글로우 효과 9가지를 모았다.
자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip
- 01 네온 텍스트 깜빡임
- 02 버튼 글로우 호버
- 03 보더 글로우 펄스
- 04 카드 스포트라이트 글로우
- 05 아이콘 글로우 링
- 06 밑줄 글로우
- 07 @property 색상 순환 글로우
- 08 light-dark() 대응 글로우
- 09 스크롤에 따른 글로우 강도
아홉 개는 인기순이 아니라 화면에서 글로우가 실제로 겪는 순서로 놓았다. 눈에 띄는 텍스트(01)로 시작해 누르는 버튼(02)·기다리는 상태 카드(03)·강조가 필요한 카드(04)·알림 아이콘(05)·메뉴 탐색(06)을 지나, 계속 튀어야 하는 배지(07)·다크모드에 맞춰야 하는 배지(08)를 거쳐 페이지에 들어오며 등장하는 카드(09)로 끝난다.
01네온 텍스트 깜빡임
글자에 겹친 다섯 겹의 text-shadow 가 네온사인처럼 번지고, 규칙적으로 켜졌다 꺼지면 가짜 티가 나서 @keyframes 정지 구간을 13개로 쪼개 불규칙하게 깜빡이게 했다.
.neon {
text-shadow:
0 0 $sp-1 $color,
0 0 $sp-2 $color,
0 0 $sp-4 rgba($color, .8),
0 0 $sp-8 rgba($color, .6),
0 0 $sp-12 rgba($color, .35);
}
.stage.is-demo .neon { animation: flicker $duration $easing infinite; }
@keyframes flicker {
0% { opacity: 1; }
6% { opacity: .3; }
14% { opacity: 1; }
20% { opacity: 1; }
26% { opacity: .4; }
34% { opacity: 1; }
50% { opacity: 1; }
56% { opacity: .25; }
64% { opacity: 1; }
80% { opacity: 1; }
86% { opacity: .35; }
94% { opacity: 1; }
100% { opacity: 1; }
}
02버튼 글로우 호버
버튼 뒤에 깔린 흐릿한 box-shadow 가 평소엔 옅게 있다가, 마우스를 올리거나 키보드로 포커스를 주면 반경과 밝기가 커진다.
.btn {
box-shadow: 0 0 8px rgba(0, 229, 255, .3),
0 0 16px rgba(0, 229, 255, .12);
transition: box-shadow .3s ease-out, transform .3s ease-out;
}
.btn:hover,
.btn:focus-visible {
box-shadow: 0 0 12px #00e5ff,
0 0 32px rgba(0, 229, 255, .65);
transform: scale(1.06);
}
03보더 글로우 펄스
카드 테두리를 감싼 box-shadow 링이 반경과 불투명도를 오가며 숨쉬듯 커졌다 작아져, 지금 이 카드가 살아 있다는 신호를 준다.
.card { animation: pulse 2s ease-in-out infinite; }
@keyframes pulse {
0%, 100% {
box-shadow: 0 0 8px rgba(57, 255, 136, .18);
border-color: rgba(57, 255, 136, .35);
}
50% {
box-shadow: 0 0 4px rgba(57, 255, 136, .7),
0 0 32px rgba(57, 255, 136, .55);
border-color: rgba(57, 255, 136, .9);
}
}
04카드 스포트라이트 글로우
카드 위 radial-gradient 의 중심을 --x --y 두 변수가 옮긴다. 이 둘을 @property 로 각도가 아니라 퍼센트 값으로 등록해 뒀기 때문에 @keyframes 가 매끄럽게 보간하고, 커서가 없을 때는 --x --y 를 keyframes 가 대신 움직인다. 실제 마우스 위치는 8줄짜리 JS 가 같은 변수에 채워 넣는다.
@property --x { syntax: '<percentage>'; inherits: false; initial-value: 50%; }
@property --y { syntax: '<percentage>'; inherits: false; initial-value: 50%; }
.card::before {
content: '';
position: absolute; inset: 0;
background: radial-gradient(circle at var(--x) var(--y),
rgba(179, 107, 255, .55), transparent 55%);
animation: sweep 2s ease-in-out infinite;
}
@keyframes sweep {
0% { --x: 18%; --y: 20%; }
25% { --x: 85%; --y: 22%; }
50% { --x: 78%; --y: 80%; }
75% { --x: 20%; --y: 76%; }
100% { --x: 18%; --y: 20%; }
}
05아이콘 글로우 링
아이콘을 감싼 원 두 개가 번갈아 커지며 옅어지는 걸 반복해, 알림이 퍼져나가는 것처럼 보인다. transform 과 opacity 만 바뀌어서 이 항목이 이 글에서 가장 가볍게 돈다.
.ring {
position: absolute; inset: 0;
border-radius: 50%;
border: 2px solid #ffb020;
opacity: 0;
animation: ring 2s ease-out infinite;
}
.ring--b { animation-delay: 1s; }
@keyframes ring {
0% { transform: scale(.7); opacity: .8; }
70% { transform: scale(1.7); opacity: 0; }
100% { transform: scale(1.7); opacity: 0; }
}
06밑줄 글로우
네온색 그라데이션이 깔린 밑줄이 고른 메뉴 아래까지 미끄러진다. 탭 두 개를 같은 96px 폭으로 고정해 뒀기 때문에, 이동 거리(96px + 간격 16px)는 렌더 결과를 재보지 않아도 계산으로 정확하다.
.tab { flex: 0 0 auto; width: $tab-w; }
.indicator {
position: absolute; left: 0; bottom: -$sp-2;
width: $tab-w; height: 3px;
background: linear-gradient(90deg, transparent, $color, transparent);
box-shadow: 0 0 $sp-3 $color, 0 0 $sp-6 rgba($color, .6);
}
// translateX 만 움직인다 — 폭이 고정값이라 도착 지점도 고정값이다
.stage.is-demo .indicator { animation: slide $duration $easing infinite; }
@keyframes slide {
0%, 40% { transform: translateX(0); }
60%, 90% { transform: translateX(#{$tab-w + $tab-gap}); } // 96px 폭 + 16px 간격
100% { transform: translateX(0); }
}
07@property 색상 순환 글로우
--hue 를 @property 로 각도(숫자) 타입으로 등록하고 0에서 360까지 돌리면 hsl(var(--hue) 90% 60%) 색이 매끄럽게 이어진다. 등록 없는 커스텀 프로퍼티는 이 보간이 안 돼 색이 중간 없이 툭 튄다.
@property --hue { syntax: '<number>'; inherits: false; initial-value: 262; }
.badge {
box-shadow: 0 0 16px hsl(var(--hue) 90% 62%),
0 0 40px hsl(var(--hue) 90% 55% / .55);
animation: cycle 2s linear infinite;
}
@keyframes cycle {
from { --hue: 0; }
to { --hue: 360; }
}
08light-dark() 대응 글로우
color-scheme 을 선언한 요소 안에서 light-dark() 로 글로우 색을 적으면, 미디어쿼리 없이도 밝은 배경에선 옅게 어두운 배경에선 짙게 한 줄이 알아서 갈린다.
.pane--light { color-scheme: light; }
.pane--dark { color-scheme: dark; }
.badge {
--glow: light-dark(rgba(124, 92, 255, .3), #7c5cff);
box-shadow: 0 0 4px var(--glow);
animation: breathe 2s ease-in-out infinite;
}
@keyframes breathe {
0%, 100% { box-shadow: 0 0 4px var(--glow); }
50% { box-shadow: 0 0 16px var(--glow); }
}
09스크롤에 따른 글로우 강도
요소가 뷰포트 중앙에 가까워질수록 IntersectionObserver 가 교차 비율을 --intensity 에 그대로 넣어 글로우가 짙어진다. 실제 스크롤이 없는 자리에서는 같은 변수를 @keyframes 가 대신 돌려 보여준다.
@property --intensity { syntax: '<number>'; inherits: false; initial-value: 0; }
.card {
box-shadow: 0 0 calc(var(--intensity) * 12px) #3ec9ff,
0 0 calc(var(--intensity) * 40px) rgba(62, 201, 255, .55);
opacity: calc(.45 + var(--intensity) * .55);
transition: box-shadow .3s linear, opacity .3s linear;
}
어디서 깨지는가 — light-dark() 는 색에만 쓴다
08번을 처음 만들 때 box-shadow 의 흐림 반경까지 light-dark(4px, 8px) 로 써서 배경마다 다르게 하려 했다. 결과는 렌더 자체가 안 됐다 — 실측으로 강도 0.0, 24프레임 중 움직인 프레임 0이 나왔다. light-dark() 는 색(<color>) 값에만 유효하고, 길이에 쓰면 그 box-shadow 선언 전체가 무효 처리돼 조용히 사라진다. 색만 light-dark() 로 넘기고 반경은 그냥 숫자로 두면 된다. zip 압축을 풀 때 묻는 비밀번호는 azk2eev6 이고, 받은 파일 그대로 입력하면 열린다. 덧붙이면 box-shadow 애니메이션은 transform·opacity 처럼 GPU 합성이 아니라 매 프레임 다시 칠하는 페인트 비용이라, 레이어가 3겹을 넘어가면 저사양 기기에서 티가 난다 — 이 글의 데모는 전부 2겹 이내로 맞췄다.
접근성
9개 전부 prefers-reduced-motion: reduce 에서 animation: none 으로 떨어진다. 움직임만 빠질 뿐 글로우 자체(색·그림자)는 남겨서, 깜빡임·펄스·순환이 불편한 사람도 무엇이 강조 대상인지는 그대로 알 수 있다. 04번 스포트라이트와 09번 스크롤 글로우는 @property 초기값으로 고정돼 갑자기 튀지 않는다. 자세한 배경은 MDN 의 reduced-motion 안내 를 참고할 만하다.
FAQ
CSS 만으로 네온 효과를 만들 수 있나요?
네, 이 9개 중 7개는 순수 CSS다. 04번(카드 스포트라이트)과 09번(스크롤 강도)만 실제 좌표·스크롤 값을 읽으려고 8줄 안팎의 짧은 JS 를 곁들였고, 그마저도 JS 없이는 자동 루프로 보이게 만들어 뒀다. 더 많은 CSS 전용 효과는 사이트 검색 에서 "glow" 로 찾아볼 수 있다.
왜 어떤 글로우는 버벅이나요?
box-shadow·filter 로 만든 글로우는 매 프레임 다시 칠해야 해서, 흐림 반경이 크고 레이어가 많을수록 무거워진다. transform·opacity 만 쓰는 애니메이션보다 비용이 크다는 걸 알고, 레이어 수를 2개 이내로 줄이는 편이 안전하다.
@property 는 어느 브라우저에서 되나요?
Chrome·Edge 는 오래전부터, Safari 는 16.4부터 지원한다. MDN 의 @property 문서 에 지원 표가 있고, 미지원 브라우저에서는 이 글의 04·07·09번 데모가 애니메이션 없이 고정된 글로우로만 보인다 — 레이아웃이 깨지지는 않는다. 사이트를 처음 본다면 소개 페이지 에서 이 블로그가 데모를 만드는 방식을 볼 수 있다.