GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

CSS 글로우 효과 9종 — 네온사인처럼 빛나는 버튼과 텍스트

CSS 글로우 효과는 버튼이나 글자 주위에 빛이 번지는 것처럼 보이게 만드는 장식이다. 네온사인처럼 빛나 보이지만 이미지나 동영상이 아니라 코드 몇 줄로 만든다. 한 줄 복붙으로 그대로 옮겨 쓸 수 있는 CSS 글로우 효과 9가지를 모았다.

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

아홉 개는 인기순이 아니라 화면에서 글로우가 실제로 겪는 순서로 놓았다. 눈에 띄는 텍스트(01)로 시작해 누르는 버튼(02)·기다리는 상태 카드(03)·강조가 필요한 카드(04)·알림 아이콘(05)·메뉴 탐색(06)을 지나, 계속 튀어야 하는 배지(07)·다크모드에 맞춰야 하는 배지(08)를 거쳐 페이지에 들어오며 등장하는 카드(09)로 끝난다. 같은 빛을 경고 신호로 쓰고 싶다면 흔들리는 애니메이션이 그 자리에 더 맞고, 빛을 칸 단위로 나눠 배치하는 구성은 벤토 그리드 호버가 보여 줍니다. 09처럼 등장과 동시에 빛나는 카드까지 오면 빛이 문서에서 맡는 구실이 전부 지나가게 됩니다.

01네온 텍스트 깜빡임

어떻게 보이나

글자에 겹친 다섯 겹의 text-shadow 가 네온사인처럼 번지고, 규칙적으로 켜졌다 꺼지면 가짜 티가 나서 @keyframes 정지 구간을 13개로 쪼개 불규칙하게 깜빡이게 했다.

네온 텍스트 깜빡임
새 창에서 열기

01 · 네온 텍스트 깜빡임 — 측정 누적면적 6.513% · 움직인 프레임 14/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

text-shadow@keyframes flicker다중 그림자

무엇이 핵심인가

.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버튼 글로우 호버

css 버튼 호버 애니메이션으로 어디에 쓰나

버튼 뒤에 깔린 흐릿한 box-shadow 가 평소엔 옅게 있다가, 마우스를 올리거나 키보드로 포커스를 주면 반경과 밝기가 커진다.

버튼 글로우 호버
새 창에서 열기

02 · 버튼 글로우 호버 — 측정 누적면적 4.303% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

box-shadow:hover · :focus-visible@keyframes

짚어 둘 점

.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 링이 반경과 불투명도를 오가며 숨쉬듯 커졌다 작아져, 지금 이 카드가 살아 있다는 신호를 준다.

보더 글로우 펄스
새 창에서 열기

03 · 보더 글로우 펄스 — 측정 누적면적 10.727% · 움직인 프레임 12/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

box-shadow 펄스border-color@keyframes

어떻게 만들었나

.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 가 대신 움직인다.

카드 스포트라이트 글로우
새 창에서 열기

04 · 카드 스포트라이트 글로우 — 측정 누적면적 20.233% · 움직인 프레임 16/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@propertyradial-gradient@keyframes sweep

코드 읽는 법

실제 마우스 위치는 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아이콘 글로우 링

어디에 쓰나

아이콘을 감싼 원 두 개가 번갈아 커지며 옅어지는 걸 반복해, 알림이 퍼져나가는 것처럼 보인다.

아이콘 글로우 링
새 창에서 열기

05 · 아이콘 글로우 링 — 측정 누적면적 6.007% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

animation-delaybox-shadow 링scale · opacity

코드의 요점

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밑줄 글로우

눈에 보이는 것

네온색 그라데이션이 깔린 밑줄이 고른 메뉴 아래까지 미끄러진다.

밑줄 글로우
새 창에서 열기

06 · 밑줄 글로우 — 측정 누적면적 3.618% · 움직인 프레임 7/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

translateXbox-shadowlinear-gradient

동작 원리

탭 두 개를 같은 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 색상 순환 글로우
새 창에서 열기

07 · @property 색상 순환 글로우 — 측정 누적면적 4.603% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@propertyhsl(var(--hue))무한 색상 순환

만드는 방식

등록 없는 커스텀 프로퍼티는 이 보간이 안 돼 색이 중간 없이 툭 튄다.

@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() 로 글로우 색을 적으면, 미디어쿼리 없이도 밝은 배경에선 옅게 어두운 배경에선 짙게 한 줄이 알아서 갈린다.

light-dark() 대응 글로우
새 창에서 열기

08 · light-dark() 대응 글로우 — 측정 누적면적 4.275% · 움직인 프레임 8/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

light-dark()color-schemebox-shadow

왜 이렇게 만들었나

.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 에 그대로 넣어 글로우가 짙어진다.

스크롤에 따른 글로우 강도
새 창에서 열기

09 · 스크롤에 따른 글로우 강도 — 측정 누적면적 1.083% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

IntersectionObserver(JS)@propertybox-shadow

구현의 핵심

실제 스크롤이 없는 자리에서는 같은 변수를 @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 선언 전체가 무효 처리돼 조용히 사라진다.

어떻게 피하나

번짐이 겹쳐 색이 탁해지는 이 지점은, 화면 한가운데 커다란 빛덩어리를 두는 AI 랜딩페이지 오브 키트에서 가장 먼저 드러납니다.

색만 light-dark() 로 넘기고 반경은 그냥 숫자로 두면 된다. zip 압축을 풀 때 묻는 비밀번호는 azk2eev6 이고, 받은 파일 그대로 입력하면 열린다. 덧붙이면 box-shadow 애니메이션은 transform·opacity 처럼 GPU 합성이 아니라 매 프레임 다시 칠하는 페인트 비용이라, 레이어가 3겹을 넘어가면 저사양 기기에서 티가 난다 — 이 글의 데모는 전부 2겹 이내로 맞췄다.

접근성

reduced-motion 에서 남는 것

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번 데모가 애니메이션 없이 고정된 글로우로만 보인다 — 레이아웃이 깨지지는 않는다. 사이트를 처음 본다면 소개 페이지 에서 이 블로그가 데모를 만드는 방식을 볼 수 있다. 빛이 카드 전체를 감싸며 반응해야 한다면 CSS 카드 호버 효과에 그 조합이 정리돼 있습니다.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.