vibeui
히어로무료새 카드

숫자가 굴러가는 SaaS 요금 페이지

월간과 연간을 바꾸면 금액이 420밀리초 동안 굴러가며 바뀐다. 가운데 카드만 어둡게 세워 테두리에 빛을 돌리고, 아래에는 머리 행이 화면에 붙는 비교표와 높이가 부드럽게 열리는 질문 목록을 둔다.

마지막 수정

프롬프트 보고 복사하기복사해서 새 대화에 그대로 붙이면 됩니다

이 프롬프트를 실제로 실행해 나온 화면입니다

영상 원본 받기결과물 HTML 받기더블클릭하면 브라우저에서 바로 열립니다
복사해서 새 대화에 그대로

붙일 프롬프트

프롬프트 언어
붙일 도구
결과 스택
Claude Code · HTML 단일 파일
55줄 · 4,868
너는 이 저장소에서 작업하는 프론트엔드 엔지니어다. 아래 지시를 읽고 먼저 계획을 3줄로 말한 뒤 바로 파일을 만들어라. 질문은 정말 막힐 때 한 번만 해라.

# 작업: 숫자가 굴러가는 SaaS 요금 페이지

## 언제 쓰는 작업인가
요금을 두 가지 주기로 받는 서비스의 요금 페이지를 한 장으로 끝낼 때 쓴다. 금액이 그냥 바뀌지 않고 세어 올라가기 때문에, 연간으로 바꿨을 때 얼마나 싸지는지가 눈으로 먼저 읽힌다.

## 준비물(이미 갖춰져 있다고 가정)
- 새 프로젝트다. Next.js 는 1번 뼈대가 깔려 있고, HTML 은 빈 index.html 에서 시작한다
- 요금제 세 개의 이름과 한 줄 설명, 월간 금액과 연간 월 환산 금액이 정해져 있다
- 비교할 기능이 아홉 줄 안팎으로 있고, 요금제마다 값이나 동그라미가 채워져 있다
- 자주 묻는 질문 다섯 개와 답이 있다. 한 답은 세 문장을 넘기지 않는다

## 건드릴 범위
요금 페이지 한 장(머리말·토글·요금제 카드 세 장·기능 비교표·질문 목록)까지다. 결제 화면과 로그인은 만들지 않고, 버튼은 눌러도 이동하지 않는다. 금액은 원화로만 보여 준다.

## 결과가 이렇게 보여야 한다
배경은 서늘한 회색 한 색이고 카드만 하얗게 떠 있다. 가운데 요금제 한 장은 검은 바탕으로 뒤집혀 있고 그 테두리를 따라 파랑에서 보라로 넘어가는 빛 한 줄기가 5.2초에 한 바퀴씩 돈다. 위쪽 알약 토글을 누르면 검은 배경이 옆으로 미끄러지고, 그와 동시에 카드 세 장의 금액이 각자 새 값까지 세어 올라가거나 내려간다. 비교표는 아래로 내려도 요금제 이름이 적힌 머리 행이 상단 바 바로 밑에 붙어 있다. 질문을 누르면 답이 아래로 밀려 나오면서 화살표가 뒤집힌다.

## 구현 순서
1. 색과 글자를 먼저 못박는다. 배경 #F4F5F7, 카드 흰색, 글자 #0E1116, 보조 글자는 같은 색 58% 투명, 경계선은 11% 투명이다. 강조는 파랑 #2B59FF 에서 보라 #8A7BFF 로 가는 그라디언트 하나뿐이고, 표의 동그라미에만 청록 #08B6A0 을 쓴다. 제목은 clamp(2.1rem, 5vw, 3.5rem) 에 letter-spacing -0.04em, 본문은 16px 에 line-height 1.6 이다. 상단 바 높이 68px 를 --nav-h 로 선언해 둔다. 뒤에서 표를 붙일 때 같은 값을 쓴다.
2. 토글은 알약 두 개에 배경 한 장이다. 감싸는 상자를 padding 4px·border-radius 999px 로 두고, 그 안에 버튼 둘을 flex: 1 1 0 으로 반씩 나눈다. 배경은 position absolute·top 4px·left 4px·width calc(50% - 4px) 인 알약 하나이고, 연간을 고르면 transform: translateX(100%) 로 옮긴다. 너비나 left 를 애니메이션하면 매 프레임 배치를 다시 계산하느라 가장자리가 떨린다. transform 은 그 계산을 건너뛴다. 전환은 380ms cubic-bezier(.2,.75,.2,1) 이다.
3. 금액은 숫자를 세어 바꾼다. 카드마다 data-m 과 data-y 에 월간·연간 값을 정수로 넣어 두고, 지금 보이는 값을 요소에 따로 기억시킨다. 바꿀 때는 requestAnimationFrame 안에서 경과 시간을 420 으로 나눠 진행도를 구하고, 1 - (1 - t)³ 인 easeOutCubic 을 곱해 시작값과 끝값 사이를 채운다. 매 프레임 Math.round 로 정수를 만든 뒤 toLocaleString('ko-KR') 로 찍으면 천 단위 쉼표가 붙는다. 다음 전환이 시작될 때 앞의 프레임 요청을 cancelAnimationFrame 으로 끊는다. 숫자 칸에는 font-variant-numeric: tabular-nums 와 min-width 2ch 를 줘서 세는 동안 폭이 흔들리지 않게 한다.
4. 금액 아래 안내 문구는 세지 않고 한 번 흐려졌다 바뀐다. data-m 과 data-y 에 두 문구를 넣어 두고, 바꿀 때 opacity 를 0 으로 260ms 보낸 뒤 200ms 지점에서 글자만 갈아 끼우고 다시 1 로 돌린다. 세는 숫자를 화면 낭독기가 매 프레임 읽으면 시끄럽기 때문에, 금액 칸은 aria-hidden 으로 감추고 눈에 보이지 않는 한 줄을 카드마다 둔다. 그 한 줄은 다 센 뒤인 480밀리초 지점에 '팀으로 쓰기 요금제, 연간 결제 기준 월 23,200원' 처럼 한 번만 채운다.
5. 강조 카드의 테두리 광선은 각도를 먼저 선언한다. @property --beam 을 syntax '<angle>'·initial-value 0deg 로 선언하지 않으면 conic-gradient 의 각도가 보간되지 않아 화면에 아무 변화가 없다. 카드 안에 inset 0·overflow hidden 인 상자를 하나 더 두고, 그 ::before 를 inset -60% 로 키워 conic-gradient(from var(--beam), transparent 0deg, #2B59FF 26deg, #8A7BFF 46deg, transparent 72deg) 를 칠한 뒤 5.2초 linear 로 돌린다. 광선을 자르는 상자를 따로 두는 이유는, 카드 자체에 overflow hidden 을 걸면 위로 11px 튀어나온 '가장 많이 고릅니다' 배지가 같이 잘리기 때문이다.
6. 비교표는 머리 행을 position: sticky·top: var(--nav-h)·z-index 10 으로 붙이고 배경색을 꼭 채운다. 배경이 없으면 밑을 지나가는 줄이 글자에 겹쳐 보인다. 여기서 한 가지를 조심한다. 표나 표를 감싼 상자에 overflow 를 걸면 그 상자가 스크롤 상자가 되어, 머리 행이 페이지가 아니라 그 상자에 붙어 버린다. 그래서 border-collapse: separate 로 두고 둥근 모서리는 머리 칸에 border-radius 를 직접 준다. 가로로 미는 것은 760px 아래에서만 켜고, 그때는 머리 행을 position: static 으로 되돌린다.
7. 질문 목록은 <details> 와 <summary> 로 만든다. 답을 감싼 상자를 display: grid·grid-template-rows: 0fr 로 두고 열렸을 때만 1fr 로 바꾼다. 안쪽 자식에는 min-height 0 과 overflow hidden 이 함께 있어야 접힌다. height: auto 는 전환되지 않기 때문에 이 방법을 쓴다. 닫을 때는 summary 의 기본 동작을 preventDefault 로 막고 .closing 을 붙여 0fr 로 보낸 뒤, 360ms 가 지난 다음에 open 을 뗀다. 바로 떼면 내용이 사라져 닫히는 모습이 아예 보이지 않는다. 하나를 열면 나머지는 같은 방식으로 접는다.
8. 섹션은 IntersectionObserver 로 한 번만 올린다. threshold 0.1·rootMargin '0px 0px -6% 0px' 로 보고, 나타난 요소는 곧바로 unobserve 한다. 반응형은 세 군데에서 꺾는다. 940px 아래에서 요금제 카드를 한 줄로 세우고 최대 폭 420px 로 가운데 모은다. 760px 아래에서 표를 가로로 밀 수 있게 하고 최소 폭 620px 를 준다. 420px 아래에서 좌우 여백을 16px 로 줄이고 토글 버튼의 최소 폭을 푼다.
9. prefers-reduced-motion 이면 모든 animation 과 transition 을 none 으로 덮고, scroll-behavior 도 auto 로 되돌린다. 금액은 세지 않고 끝값을 바로 넣고, 문구도 흐림 없이 갈아 끼우고, 질문은 기다림 없이 접는다. 400px 과 1440px 에서 가로 스크롤이 없는지 보고 빌드를 통과시킨다.

## 기본값(필요하면 바꿔라)
- 금액을 세는 시간: 420ms (바꿀 수 있는 범위: 260ms ~ 700ms(길면 다 세기 전에 다시 누르게 된다))
- 토글 배경이 미끄러지는 시간: 380ms (바꿀 수 있는 범위: 220ms ~ 500ms)
- 테두리 광선 한 바퀴: 5.2초 (바꿀 수 있는 범위: 3초 ~ 9초)
- 질문이 열리는 시간: 360ms (바꿀 수 있는 범위: 220ms ~ 500ms(늦으면 두 번 누르게 된다))
- 머리 행이 붙는 높이: 68px (바꿀 수 있는 범위: 상단 바 높이와 같은 값(바가 없으면 0))

## 하지 말 것
- 금액을 innerHTML 로 한 번에 갈아 끼우지 않는다. 연간이 얼마나 싼지가 눈에 남지 않는다.
- 표나 표를 감싼 상자에 overflow 를 걸지 않는다. 머리 행이 페이지 대신 그 상자에 붙어 제자리에 멈춘다.
- 붙은 머리 행에 배경색을 빼먹지 않는다. 밑을 지나가는 줄이 글자 위로 겹쳐 보인다.
- @property 선언 없이 conic-gradient 의 각도를 애니메이션하지 않는다. 보간이 안 돼 광선이 서 있기만 한다.
- 답 상자의 높이를 height: auto 로 열지 않는다. 전환되지 않아 그냥 튀어나온다.
- 세는 숫자를 aria-live 로 읽히지 않는다. 한 번 누를 때마다 수십 번을 읽어 준다.

## 출력 규칙 (HTML 단일 파일)
- 결과는 index.html 파일 하나로 저장한다. 외부 CDN 을 쓰지 않고 CSS·JS 를 파일 안에 넣는다.
- HTML 파일 하나로 완결한다. 별도 CSS·JS 파일을 만들지 않는다.
- 폰트는 시스템 폰트 스택을 기본으로 쓰고, 한글은 'Pretendard Variable', 'Apple SD Gothic Neo', 'Malgun Gothic' 순으로 폴백한다.
- 색·간격·둥글기는 :root 의 CSS 변수로 모아 두고, 본문에서는 변수만 참조한다.
- 1200px 데스크톱과 400px 모바일에서 가로 스크롤이 생기지 않아야 한다.
- 움직임은 prefers-reduced-motion 을 존중한다.

끝나면 만든 파일 목록과 브라우저에서 확인할 URL 을 한 줄씩 적어라.

복사한 뒤 Claude Code 새 대화에 그대로 붙이면 됩니다. 아래 값은 붙이기 전에 숫자만 바꿔도 결과가 달라집니다.

바꿔 볼 값

  • 금액을 세는 시간420ms

    260ms ~ 700ms(길면 다 세기 전에 다시 누르게 된다)

  • 토글 배경이 미끄러지는 시간380ms

    220ms ~ 500ms

  • 테두리 광선 한 바퀴5.2초

    3초 ~ 9초

  • 질문이 열리는 시간360ms

    220ms ~ 500ms(늦으면 두 번 누르게 된다)

  • 머리 행이 붙는 높이68px

    상단 바 높이와 같은 값(바가 없으면 0)

붙이기 전에 한 번

언제 쓰고, 뭐가 필요한가

언제 쓰나

요금을 두 가지 주기로 받는 서비스의 요금 페이지를 한 장으로 끝낼 때 쓴다. 금액이 그냥 바뀌지 않고 세어 올라가기 때문에, 연간으로 바꿨을 때 얼마나 싸지는지가 눈으로 먼저 읽힌다.

준비물

  • 새 프로젝트다. Next.js 는 1번 뼈대가 깔려 있고, HTML 은 빈 index.html 에서 시작한다
  • 요금제 세 개의 이름과 한 줄 설명, 월간 금액과 연간 월 환산 금액이 정해져 있다
  • 비교할 기능이 아홉 줄 안팎으로 있고, 요금제마다 값이나 동그라미가 채워져 있다
  • 자주 묻는 질문 다섯 개와 답이 있다. 한 답은 세 문장을 넘기지 않는다
복사해서 붙인 직후

결과는 이렇게 나옵니다

연간 결제를 누르면 29,000 이 23,200 까지 세어 내려가고 89,000 은 71,200 에서 멈추며, 그 아래 문구가 '1년치 278,400원을 한 번에 결제합니다' 로 바뀐다. 내려가면 표 머리 행이 상단 바 밑에 붙어 따라오고, 질문을 누르면 답이 밀려 나오면서 앞서 열려 있던 답은 접힌다.

같이 쓰면 좋습니다

이어서 붙이면 좋은 카드