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

CSS 텍스트 효과 9종 — 해킹처럼 풀리는 디코드 연출

CSS 텍스트 효과는 제목이나 문장이 화면에 나타날 때 함께 움직이는 연출로, 코드를 몰라도 파일을 그대로 붙이면 바로 쓸 수 있습니다. 이 아홉 가지는 해킹 화면처럼 문장이 풀리는 순간을 노려, 헥스 코드나 이진수·암호가 뒤섞였다가 제자리를 찾아가는 과정을 보여줍니다.

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

아홉 개는 인기 순이 아니라 감춰진 정보가 드러나는 순서를 따라갑니다. 먼저 무작위 코드가 진짜 글자로 맞춰지는 연출(01~03)을 지나, 점이나 검은 막대로 가려진 정보가 벗겨지는 연출(04~06)을 거쳐, 마지막은 검증·발각의 순간을 연출하는 효과(07~09)로 끝납니다. 01~03의 코드가 한 글자씩 맞춰지는 흐름을 문장이 아니라 카드 한 장 단위로 옮겨 놓으면 CSS 카드 효과가 됩니다. 같은 feedback 기둥에서 글자 대신 표면의 광택만 따로 떼어 다루는 쪽은 CSS 홀로그램 카드 효과인데, 04~06번이 가림막을 벗겨 정보를 드러내는 것과는 반대로 빛을 한 겹 덮어 정보를 가리는 방향입니다.

01헥스 코드 정착

css 글자 효과로 코드를 맞춘다

글자 자리마다 무작위 16진수 한 글자가 빠르게 반짝이다가, 오른쪽부터 하나씩 실제 글자로 멈춰 섭니다. 정착 전엔 주황, 정착 후엔 초록으로 색도 함께 바뀝니다.

헥스 코드 정착
새 창에서 열기

01 · 헥스 코드 정착 — 측정 누적면적 2.084% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

setIntervalJS 10줄color transition

자바스크립트가 필요한 이유

CSS만으로는 글자 내용을 바꿀 수 없어서 짧은 자바스크립트로 처리했습니다.

var target = "ACCESS", hex = "0123456789ABCDEF";
var spans = document.querySelectorAll(".hb-c");
var n = 0;
setInterval(function () {
  n++;
  var settled = Math.max(0, Math.floor((n % 44) / 5) - 1);
  spans.forEach(function (s, i) {
    var on = i >= target.length - settled;
    s.textContent = on ? target[i] : hex[Math.random() * 16 | 0];
    s.className = on ? "hb-c hb-on" : "hb-c hb-off";
  });
}, 90);

02이진수 비트 스왑

이진수에서 글자로

글자가 처음엔 01001000 같은 이진수 여덟 자리로 보이다가, 순간적으로 사라지며 실제 글자가 겹쳐 떠오릅니다.

이진수 비트 스왑
새 창에서 열기

02 · 이진수 비트 스왑 — 측정 누적면적 1.574% · 움직인 프레임 13/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

opacity crossfadeJS 9줄animation-delay stagger

스태거는 inherit 로

왼쪽부터 순서대로 바뀌는데, 이 스태거는 자바스크립트가 아니라 부모 요소의 인라인 animation-delay를 자식이 inherit로 물려받아 만듭니다.

.bn-ch {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(.85);
}

@keyframes bn-ch-in {
  0%, 40% { opacity: 0; transform: scale(.85); }
  55%, 90% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(.85); }
}

.bn-word.is-demo .bn-ch {
  animation: bn-ch-in 2s ease-out infinite;
  animation-delay: inherit;
}

03알파벳 순환 암호

한 칸씩 되감는다

각 글자가 알파벳 몇 칸 뒤에서 출발해 한 칸씩 되감기며 원래 글자를 찾아갑니다.

알파벳 순환 암호
새 창에서 열기

03 · 알파벳 순환 암호 — 측정 누적면적 2.021% · 움직인 프레임 10/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

charCodeAt 연산JS 10줄textContent 교체

자물쇠 다이얼처럼

자물쇠 다이얼을 맞추는 느낌으로, 글자마다 정해둔 오프셋 값을 주기적으로 되풀이합니다.

var offs = [5, 6, 4, 7, 5, 6], cyc = 40;
var n = 0;
setInterval(function () {
  n++;
  spans.forEach(function (s, i) {
    var t = ((n - i * 2) % cyc + cyc) % cyc;
    var left = Math.max(0, offs[i] - Math.floor(t / 2));
    var base = target.charCodeAt(i) - 65;
    s.textContent = String.fromCharCode(65 + (base + left) % 26);
  });
}, 130);

04비밀번호 점 모핑

점 뒤에 숨어 있다가

비밀번호 입력창처럼 동그란 점들로 가려져 있던 글자가, 살짝 커지며 점이 사라지고 실제 글자가 드러납니다.

비밀번호 점 모핑
새 창에서 열기

04 · 비밀번호 점 모핑 — 측정 누적면적 1.936% · 움직인 프레임 10/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

position:absolute stackopacity+scale@keyframes pd-show

두 레이어의 크로스페이드

점 레이어와 글자 레이어를 겹쳐 두고 opacity와 scale만 반대로 움직이는 크로스페이드입니다.

.pd-real {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(.9);
}

@keyframes pd-show {
  0%, 18% { opacity: 0; transform: scale(.9); }
  48%, 78% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(.9); }
}

.pd-wrap.is-demo .pd-real { animation: pd-show 2s cubic-bezier(.2, .8, .2, 1) infinite; }

05스캔라인 마스크 스윕

스캔라인이 훑고 지나가면

지지직거리는 노이즈 블록이 글자를 덮고 있다가, 밝은 스캔라인이 위에서 아래로 훑고 지나가면 그 자리부터 또렷한 글자가 드러납니다.

스캔라인 마스크 스윕
새 창에서 열기

05 · 스캔라인 마스크 스윕 — 측정 누적면적 6.767% · 움직인 프레임 19/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

clip-path: inset()repeating-linear-gradient@keyframes sl-beam

clip-path 를 넓혀 걷는다

노이즈 레이어의 clip-path를 위에서 아래로 넓혀가며 걷어내는 방식입니다.

.sl-noise {
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(45deg, rgba(255, 255, 255, .4) 0 2px, transparent 2px 4px);
  mix-blend-mode: screen;
}

@keyframes sl-clear {
  0%, 8% { clip-path: inset(0 0 0 0); }
  55%, 78% { clip-path: inset(100% 0 0 0); }
  100% { clip-path: inset(0 0 0 0); }
}

.sl-wrap.is-demo .sl-noise { animation: sl-clear 2s ease-out infinite; }

06기밀 해제 바 리프트

검정 막대가 걷힌다

기밀 문서처럼 검정 막대가 단어를 하나씩 가리고 있다가, 오른쪽부터 걷히며 순서대로 글자가 드러납니다.

기밀 해제 바 리프트
새 창에서 열기

06 · 기밀 해제 바 리프트 — 측정 누적면적 4.215% · 움직인 프레임 19/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@keyframes rb-liftscaleX transform-originanimation-delay stagger

왼쪽 단어부터 차례로

두 단어 사이에 지연 시간을 살짝 줘서 왼쪽 단어부터 차례로 해제됩니다.

.rb-bar {
  position: absolute;
  inset: -8% -6%;
  background: $stage-ink;
  transform-origin: right center;
}

@keyframes rb-lift {
  0%, 12% { transform: scaleX(1); }
  50%, 78% { transform: scaleX(0); }
  100% { transform: scaleX(1); }
}

.rb-wrap.is-demo .rb-word .rb-bar { animation: rb-lift 2s ease-out infinite; }
.rb-wrap.is-demo .rb-word--2 .rb-bar { animation-delay: .3s; }

07체크섬 카운트다운

진행률에 맞춰 채워진다

처음엔 밑줄만 나열돼 있다가 옆의 진행률 숫자가 올라가는 것과 맞춰 왼쪽부터 글자가 하나씩 채워지고, 채워질 때마다 살짝 튕깁니다.

체크섬 카운트다운
새 창에서 열기

07 · 체크섬 카운트다운 — 측정 누적면적 1.409% · 움직인 프레임 16/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

JS 10줄content 채우기scale pop

퍼센트는 어떻게 계산하나

채워진 글자 수를 그대로 퍼센트로 계산해 보여줍니다.

var n = 0;
setInterval(function () {
  n++;
  var filled = Math.min(target.length, Math.floor((n % 50) / 5));
  spans.forEach(function (s, i) {
    s.textContent = i < filled ? target[i] : "_";
    s.classList.toggle("cc-pop", i === filled - 1);
  });
  pct.textContent = Math.round(filled / target.length * 100) + "%";
}, 90);

08네거티브 반전 플래시

네거티브가 몇 번 깜빡인다

색이 반전된 네거티브 필름 같은 글자가 두세 번 깜빡이다가 사라지면, 그 아래 정상 색의 진짜 글자가 남습니다.

네거티브 반전 플래시
새 창에서 열기

08 · 네거티브 반전 플래시 — 측정 누적면적 4.139% · 움직인 프레임 8/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@keyframes ni-flashopacity flicker keyframesposition:absolute overlay

반전 레이어 하나로

반전 레이어 하나를 원래 글자 위에 겹치고 opacity만 여러 번 껐다 켭니다.

.ni-negative {
  position: absolute;
  inset: 0;
  color: $stage-paper;
  background: $color;
}

@keyframes ni-flash {
  0%, 4% { opacity: 1; }
  9% { opacity: 0; }
  14% { opacity: 1; }
  19% { opacity: 0; }
  24% { opacity: 1; }
  32%, 100% { opacity: 0; }
}

.ni-wrap.is-demo .ni-negative { animation: ni-flash 2s ease-out infinite; }

09레이더 스윕 채움

부채꼴 선이 한 바퀴

레이더 화면처럼 밝은 부채꼴 선이 글자 위를 한 바퀴 돌면서 지나간 자리의 글자만 순서대로 선명해집니다.

레이더 스윕 채움
새 창에서 열기

09 · 레이더 스윕 채움 — 측정 누적면적 10.213% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

conic-gradienttransform: rotateanimation-delay per letter

SCSS 반복문으로 나눈 지연

글자마다 스윕이 지나가는 타이밍에 맞춰 animation-delay를 SCSS 반복문으로 나눠 줬습니다.

.rs-sweep {
  position: absolute;
  inset: -60%;
  background: conic-gradient(from 0deg, transparent 0deg, rgba(61, 220, 132, .5) 10deg, transparent 34deg);
  mix-blend-mode: screen;
}

@keyframes rs-lock {
  0%, 4% { opacity: .16; }
  14%, 88% { opacity: 1; }
  100% { opacity: .16; }
}

@for $i from 1 through 8 {
  .rs-wrap.is-demo .rs-text span:nth-child(#{$i}) {
    animation-delay: calc(2s / 8 * #{$i - 1});
  }
}

CSS 텍스트 효과는 어디서 깨지는가

24프레임 중 5장뿐이었다

가장 먼저 걸려 넘어진 건 04번이었습니다. 점과 글자를 크로스페이드로 겹쳐 두면서 전환 구간을 전체 주기의 42~58%로 좁게 잡았더니, 24프레임 중 실제로 픽셀이 바뀌는 프레임이 5장뿐이라 최소 기준(6장)에 못 미쳤습니다. 겉보기엔 멀쩡히 도는 것 같았지만 프레임을 하나씩 열어 보니 대부분의 프레임이 완전히 같은 모양이었습니다. 전환 구간을 18~48%로 넓히고 나서야 24프레임 중 10장이 움직임을 담아 기준을 통과했습니다.

가장 적게·가장 많이 움직인 항목

렌더러가 프레임 사이 픽셀 차이로 "움직였는가"를 판단하기 때문에, 순간적으로 끝나는 크로스페이드는 눈에는 자연스러워 보여도 측정값으로는 정지 화면과 구별이 안 될 수 있다는 뜻입니다. 같은 원리로 08번 네거티브 반전 플래시도 깜빡이는 구간이 짧아 움직인 프레임이 8장으로 아홉 개 중 가장 적었지만, 그래도 최소 기준(6장)은 넉넉히 넘겼습니다. 반대로 09번 레이더 스윕 채움은 23장 모두가 움직여, 01번 헥스 코드 정착과 함께 아홉 개 중 가장 오래 움직이는 항목이었습니다. 화면 전체를 계기판처럼 덮는 HUD 디자인 부품도 스캔라인이 짧게 스치는 구간에서 08번과 똑같은 측정 사각지대를 만듭니다. 이 zip을 열 때 쓰는 비밀번호는 8y6rrjcm 이고, 아홉 번째 항목까지 다 읽고 나면 자연스럽게 마주치게 됩니다.

접근성 — 아홉 개가 reduced-motion에서 남기는 것

멈춘 최종 모습으로 본다

화면에서 움직임을 줄이도록 설정해 둔 사람은 이 아홉 개 전부를 prefers-reduced-motion: reduce 조건 아래 정지된 최종 모습으로 봅니다. 02·04·05·06·08·09번처럼 SCSS @keyframes로 움직이는 항목은 최종 모습(글자가 다 드러나고, 막대가 걷히고, 스윕이 끝난 상태)을 !important로 고정해 곧바로 다 보이게 했습니다.

자바스크립트 항목은 따로

01·03·07번은 CSS가 아니라 자바스크립트로 움직이기 때문에, 스크립트 안에서 matchMedia로 직접 검사해 무작위로 섞지 않고 완성된 단어를 바로 보여줍니다.

더 읽을 거리

비슷한 등장 효과가 궁금하다면 CSS 텍스트 애니메이션 9종 글도 참고할 만하고, css 텍스트 타이핑 효과가 궁금하다면 CSS 타이핑 텍스트 효과 글을 보면 됩니다. 이 아홉 개 말고 다른 주제의 모음도 함께 훑어보고 싶다면 CSS 애니메이션 카테고리 페이지에서 둘러볼 수 있습니다. 겹친 레이어를 잘라내는 clip-path 속성의 세부 값은 MDN 레퍼런스에 정리돼 있습니다.

FAQ — 자주 묻는 질문

텍스트 애니메이션 효과에 자바스크립트가 꼭 필요한가요?

대부분은 아닙니다. 아홉 개 중 여섯 개는 SCSS @keyframes와 opacity·transform·clip-path만으로 동작하고, 01·03·07번 세 개만 글자 내용 자체를 무작위로 바꾸거나 채워야 해서 짧은 자바스크립트가 들어갑니다.

01번 헥스 코드 정착과 03번 알파벳 순환 암호 중 어느 걸 써야 하나요?

로그인 성공이나 보안 인증처럼 "코드가 풀린다"는 느낌이 중요하면 01번이 더 기술적으로 보이고, 퍼즐이나 잠금 해제처럼 "차례로 맞춰진다"는 느낌이 중요하면 03번의 자물쇠 다이얼 방식이 더 잘 어울립니다. 문장을 조용히 세워 두는 쪽은 인용구 박스 디자인에 모아 두었습니다.

zip 안의 vanilla와 react 코드가 다른가요?

움직임 자체는 vanilla와 React 두 버전이 동일하게 만들어 냅니다. 차이는 재생 방식뿐인데, vanilla 데모는 is-demo 클래스로 끊임없이 반복시키지만 React 컴포넌트는 IntersectionObserver를 써서 화면에 들어온 순간 딱 한 번만 움직이도록 짜여 있습니다.

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

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