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

이벤트 페이지 템플릿 9종 — 그대로 붙이는 섹션

이벤트 페이지 템플릿은 컨퍼런스·밋업 랜딩 페이지에 바로 붙이는 히어로·일정·연사·가격 같은 섹션을 통째로 담은 코드 모음입니다. 아홉 개 중 하나는 실제 목표 시각까지 초 단위로 줄어드는 카운트다운을 담았습니다.

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

아홉 개는 인기 순이 아니라 방문자가 랜딩 페이지를 훑는 순서대로 놓았습니다. 먼저 눈길을 끌고(히어로) 일정을 확인시키고(타임라인) 신뢰를 쌓은 뒤(연사) 실행 정보를 주고(장소) 구매를 결정시키고(가격) 신뢰를 한 번 더 보강하고(스폰서) 남은 의심을 없앤 다음(FAQ) 전환시키고(등록 CTA) 마지막으로 지금 해야 할 이유를 못 박습니다(카운트다운). 사진·로고 자리는 전부 이 사이트의 먹·파랑·크림 토큰으로 만든 플레이스홀더 도형이라, 실제 로고와 사진만 갈아 끼우면 이벤트 상세페이지 디자인이 그대로 완성됩니다. 행사가 끝난 뒤 보도자료를 모아 둘 자리가 필요하면 프레스룸 섹션 템플릿이 이어지고, 참가자 현황을 숫자로 보여 줄 판은 대시보드 위젯 UI에 있습니다.

01이벤트 히어로

히어로 섹션 디자인의 뼈대

무대 뒤 흐릿한 원 세 개가 계속 떠다니는 동안 워드마크(도형 플레이스홀더)·헤드라인·날짜/장소 알약·CTA가 한 화면에 자리 잡습니다.

이벤트 히어로
새 창에서 열기

01 · 이벤트 히어로 — 측정 누적면적 30.31% · 움직인 프레임 18/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

transformopacityaria-pressed

관심 등록 버튼은 진짜다

오른쪽 "관심 등록" 버튼은 클릭마다 aria-pressed가 실제로 바뀌며 아이콘이 채워집니다.

.eh__orb--1 { animation: eh-float-a 2s ease-in-out infinite; }
@keyframes eh-float-a {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50%      { transform: translate($sp-10, -$sp-8) scale(1.5); }
}
.eh__save[aria-pressed="true"] {
  background: rgba(255, 247, 230, .12);
  border-color: $subject-cream;
}
.eh__save[aria-pressed="true"] .eh__save-ic {
  opacity: 1;
  color: $color;
  transform: scale(1.15);
}

02일정 타임라인

클릭한 지점까지 채워진다

세로선 위 정류장(등록·기조연설·네트워킹·패널)을 클릭하면 그 지점까지 진행바가 scaleY로 실제로 채워집니다.

일정 타임라인
새 창에서 열기

02 · 일정 타임라인 — 측정 누적면적 1.099% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

scaleYclickaria-current

지금 연 정류장만 강조

지금 연 정류장의 점만 펄스 링으로 강조됩니다.

.tl__fill {
  position: absolute; inset: 0;
  transform: scaleY(.25);
  transform-origin: top;
  background: $color;
  transition: transform $duration $easing;
}
.tl__stop[aria-current="step"] .tl__dot {
  background: $color;
  transform: scale(1.3);
  box-shadow: 0 0 0 4px rgba(23, 20, 26, .14);
}

03연사 그리드

여섯 장이 순서대로 떠오른다

카드 여섯 장이 순서대로 살짝 기울었다 펴지며 떠오릅니다.

연사 그리드
새 창에서 열기

03 · 연사 그리드 — 측정 누적면적 40.452% · 움직인 프레임 13/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

staggergrid-template-columnsclick

클릭한 카드만 펼쳐진다

카드를 클릭하면 그 카드만 한 줄짜리 소개가 max-height 전환으로 펼쳐지고 나머지는 그대로 닫혀 있습니다.

.sg.is-demo .sg__card { animation: sg-in 2s $easing infinite; }
@keyframes sg-in {
  0%   { opacity: .7; transform: translateY(6px) rotate(-1.5deg); }
  18%  { opacity: 1; transform: translateY(0) rotate(0deg); }
  86%  { opacity: 1; transform: translateY(0) rotate(0deg); }
  100% { opacity: .7; transform: translateY(6px) rotate(-1.5deg); }
}
.sg__card.is-open .sg__bio { max-height: 60px; opacity: 1; }

04장소 안내

탭으로 이동수단을 고른다

지하철·버스·자차 탭을 누르면 그 이동수단 설명만 실제로 보이고, 밑줄 인디케이터가 고른 탭 아래까지 미끄러집니다. 오른쪽 지도 플레이스홀더 위 핀은 계속 파문을 그립니다.

장소 안내
새 창에서 열기

04 · 장소 안내 — 측정 누적면적 2.01% · 움직인 프레임 20/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

opacityaria-selectedclick

상태가 바뀌는 방식

여기는 :checked 대신 진짜 버튼 클릭으로 상태가 바뀝니다.

tabs.forEach(function (t) {
  t.addEventListener('click', function () {
    tabs.forEach(function (o) { o.setAttribute('aria-selected', 'false'); });
    t.setAttribute('aria-selected', 'true');
    ind.style.transform = 'translateX(' + (Number(t.dataset.i) * 100) + '%)';
    panels.forEach(function (p, i) {
      p.style.opacity = String(i === Number(t.dataset.i) ? 1 : 0);
    });
  });
});

05티켓 가격표

가운데 카드만 맴도는 글로우

가운데 카드에 "인기" 배지와 은은한 글로우가 계속 맴돕니다.

티켓 가격표
새 창에서 열기

05 · 티켓 가격표 — 측정 누적면적 4.375% · 움직인 프레임 8/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

scaleopacityaria-checked

스위치로 가격이 바뀐다

위쪽 얼리버드/정가 스위치를 누르면 세 카드의 가격 숫자가 한 번에 크로스페이드로 바뀝니다.

.tp__price-a, .tp__price-b {
  position: absolute; inset: 0;
  transition: opacity $duration $easing;
}
.tp__price-b { opacity: 0; }
.tp__switch[aria-checked="true"] ~ .tp__cards .tp__price-a { opacity: 0; }
.tp__switch[aria-checked="true"] ~ .tp__cards .tp__price-b { opacity: 1; }
.tp__card--pop { animation: tp-glow 2.4s ease-in-out infinite; }

06스폰서 로고 바

끊김 없이 흐르는 로고

로고 여덟 개(전부 도형 플레이스홀더, 실제 로고 아님)가 트랙을 이어 붙여 끊김 없이 흐릅니다.

스폰서 로고 바
새 창에서 열기

06 · 스폰서 로고 바 — 측정 누적면적 8.057% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

translateX(-50%)animation-play-statepointerenter

올리면 그 자리에서 멈춘다

마우스를 올리거나 버튼을 탭하면 그 자리에서 멈춥니다.

// 트랙 자체엔 gap 을 쓰지 않는다 — 항목마다 margin-right 로 간격을 준다
.sb__track { display: flex; width: max-content; animation: sb-scroll 14s linear infinite; }
.sb__logo { flex: 0 0 auto; margin-right: $sp-6; }
@keyframes sb-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

07이벤트 FAQ

한 번에 하나만 열린다

질문 세 개 중 하나만 열려 있습니다.

이벤트 FAQ
새 창에서 열기

07 · 이벤트 FAQ — 측정 누적면적 28.795% · 움직인 프레임 10/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

grid-template-rowsaria-expandedclick

펼쳐지는 방식

클릭한 질문의 답변 칸이 grid-template-rows로 실제로 펼쳐지고 나머지는 닫히며, 화살표가 함께 돕니다. 행사 대신 결과물을 앞세우는 페이지라면 프로젝트 쇼케이스 섹션 템플릿이 그 구성입니다.

.fq__a-wrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows $duration $easing;
}
.fq__item.is-open .fq__a-wrap { grid-template-rows: 1fr; }
.fq__a { min-height: 0; overflow: hidden; }

08등록 CTA

형식을 진짜로 검사한다

이메일을 넣고 버튼을 누르면 실제로 형식을 검사합니다.

등록 CTA
새 창에서 열기

08 · 등록 CTA — 측정 누적면적 0.857% · 움직인 프레임 9/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

stroke-dashoffsetopacitysubmit

통과와 실패의 신호

통과하면 버튼에 체크가 그려지고 아래에 "등록 완료" 안내 문구가 뜨며, 형식이 틀리면 입력칸이 좌우로 짧게 흔들립니다.

form.addEventListener('submit', function (e) {
  e.preventDefault();
  var ok = /^\S+@\S+\.\S+$/.test(input.value);
  if (ok) {
    form.classList.add('is-done');
    done.textContent = '등록 완료 — 확인 메일을 보냈습니다.';
  } else {
    input.classList.remove('is-shake');
    void input.offsetWidth;
    input.classList.add('is-shake');
    done.textContent = '올바른 이메일을 입력해 주세요.';
  }
});

09카운트다운 띠

목표 시각까지 초 단위로

로드된 시점부터 정확히 7일 4시간 20분 15초 뒤를 목표 시각으로 잡고, 1초마다 실제로 숫자가 줄어듭니다.

카운트다운 띠
새 창에서 열기

09 · 카운트다운 띠 — 측정 누적면적 16.861% · 움직인 프레임 12/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

translateYsetIntervalDate

매초 살짝 튀는 초 카드

초 카드는 매초 살짝 튀어 오릅니다.

var target = Date.now() + ((7 * 24 * 60 * 60) + (4 * 60 * 60) + (20 * 60) + 15) * 1000;
function tick() {
  var diff = Math.max(0, target - Date.now());
  var s = Math.floor(diff / 1000);
  els.d.textContent = pad(Math.floor(s / 86400));
  els.h.textContent = pad(Math.floor((s % 86400) / 3600));
  els.m.textContent = pad(Math.floor((s % 3600) / 60));
  els.s.textContent = pad(s % 60);
}
tick();
setInterval(tick, 1000);

어디서 깨지는가 — 함정

좁은 화면에서 넘친다

아홉 개를 만드는 동안 진짜 걸린 함정은 화면 크기였습니다. 480×300 캔버스에서는 멀쩡하던 히어로가 처음엔 세로 여백을 너무 많이 잡아먹어 그 안에서도 스크롤바가 떴습니다. 패딩과 줄간격을 한 단계씩 줄여서야 통과했습니다. 진짜 문제는 폭 320px·높이 200px의 좁은 화면이었습니다. 연사 그리드·장소 안내·티켓 가격표·FAQ·등록 CTA 다섯 개가 이 크기에서 키가 260~450px까지 넘쳤습니다. 2열로 접는 게 능사가 아니어서, 연사 그리드는 오히려 3열을 유지하고 역할 설명 같은 2차 정보만 좁은 화면에서 숨기는 쪽으로 고쳤습니다.

3D 플립과 트랙 이음새

카운트다운 초 카드를 처음엔 rotateX(0 → -90deg → 0)로 통째로 뒤집었는데, perspective 없이 90도 지점을 지나는 순간 숫자가 완전히 납작해져 캡처 화면에 선 하나("--")처럼 찍혔습니다. 3D 플립 대신 translateY+scale로 살짝 튀는 정도로 바꾸자 해결됐습니다. 행사 날짜가 지나면 남는 카운트다운처럼 시간이 붙은 부품은 대시보드 컴포넌트에서도 갱신 주기를 정해 두지 않으면 같은 식으로 낡습니다. 스폰서 로고 바처럼 translateX(-50%)로 무한 스크롤하는 트랙은 항목 사이 간격을 gap이 아니라 margin-right로 줘야 합니다. gap을 쓰면 두 벌(2N개) 사이 이음새가 gap ÷ 2만큼 모자라 트랙이 한 바퀴 돌 때마다 살짝 튑니다. 이 두 가지를 포함해 반응형과 애니메이션을 전부 손본 다음, 5ngtcz6h으로 최신 상태의 zip 을 열어 보면 아홉 개가 위 표대로 정확히 동작합니다.

접근성

동작 줄이기에서도 상태는 남는다

아홉 개 전부 prefers-reduced-motion: reduce에서 장식 전환·애니메이션을 끄되 상태 자체는 남깁니다 — 일정 진행바는 채워진 채로, FAQ 답변은 열린 채로 고정되고 카운트다운 숫자 갱신만은 그대로 유지합니다.

탭과 상태 알림

장소 안내(04)는 role="tablist"·role="tab"·aria-selected를 실제로 갖춘 탭이고, 등록 CTA(08)의 결과 문구는 role="status"(aria-live)라 스크린 리더가 제출 결과를 바로 읽습니다. 카운트다운(09)은 role="timer"에 aria-live="off"를 줘서 매초 갱신되는 숫자가 스크린 리더를 방해하지 않게 했습니다.

더 읽을 거리

탭·아코디언은 표준 button 요소로 만들어 Tab·Enter로 그대로 조작되고, role과 상태 속성은 W3C ARIA Authoring Practices의 탭 패턴을 참고했습니다. 다른 랜딩 페이지 섹션 소재는 템플릿 카테고리에 모여 있고, 이 사이트가 무엇을 다루는지는 소개에 있습니다.

FAQ

이벤트 랜딩페이지에 실제 로고나 사진을 써도 되나요?

네, 그게 목적입니다. 아홉 개 전부 사진·로고 자리는 도형 플레이스홀더라 실제 이미지·로고로 갈아 끼우기만 하면 됩니다. 색은 demo-tokens.scss 값을 그대로 써도 되고, 브랜드 색으로 바꿔도 레이아웃은 그대로 유지됩니다.

React 버전은 CSS 버전과 뭐가 다른가요?

동작은 같습니다. 활성 탭·열린 아코디언 같은 상태는 useState로 들고, 변수 3개(duration·easing·color)는 props로 받아 CSS 변수(--duration --easing --color)로 넘깁니다. 카운트다운은 useEffect 안에서 같은 setInterval 로직을 그대로 씁니다.

카운트다운이 실제로 작동하나요, 아니면 장식인가요?

실제로 작동합니다. 페이지를 연 시점부터 정확히 7일 4시간 20분 15초 뒤를 목표로 잡고 1초마다 갱신됩니다. 목표 시각은 targetMs 값 하나만 실제 행사 날짜로 바꾸면 그대로 쓸 수 있습니다. 자동 재생 GIF에서 보이는 초 카드의 튀는 동작은 별도 @keyframes 장식이고, 숫자 갱신과는 독립적으로 작동합니다.

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

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