GODRICH
#css-animation#button#loader#transition#scroll#dashboard#ui#design-trends

9 Event Landing Page Templates — Copy-Paste

Event landing page templates are ready-to-paste hero, schedule, speaker, and pricing sections for conference and meetup pages, and one of the nine below counts

Auto-plays · click a tile to jump to its section · all nine in one zip

The nine aren't ranked by popularity — they're ordered the way a visitor actually scrolls a landing page. A hero grabs attention first, a timeline sets expectations, a speaker grid builds trust, a venue block hands over logistics, a pricing table asks for a decision, a sponsor bar reinforces trust once more, an FAQ clears the last doubts, a register CTA converts, and a countdown band nails down why it has to happen now. Every photo, logo, and avatar placeholder below is a shape built from this site's own ink, blue, and cream tokens — nothing here is a real brand or a real face. Swap in an actual logo file or a photo and the layout, transitions, and JavaScript keep working exactly as they are, so the event landing page design survives the swap. For somewhere to collect the coverage once an event is over, the pressroom section templates follow on, and a panel showing attendance as numbers lives in the dashboard widget UI pieces.

01Event hero

A conference landing page hero

Three blurred circles drift behind the stage on a continuous translate-and-scale loop while the headline, date pill, and CTA row hold still in front of them.

Event hero
Open in a new tab

01 · Event hero — Measured area 30.31% · moving frames 18/23Original by GODRICH · SCSS and React ship in the zip · See the password

transformopacityaria-pressed

A bookmark that really toggles

The "save this event" bookmark button on the right actually flips aria-pressed and fills its icon on every real click, so it fits a conference or meetup landing page, or the hero of a paid ad landing page pointed at one event.

.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);
}

02Schedule timeline

Clicking fills the track

Clicking a stop on the vertical line fills the progress track up to that point with a real scaleY transform and rings the newly active dot with a box-shadow pulse, while an idle loop cycles the same four stops on its own until someone clicks.

Schedule timeline
Open in a new tab

02 · Schedule timeline — Measured area 1.099% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

scaleYclickaria-current

An idle loop until someone clicks

It works well for a same-day event agenda or a conference schedule block sitting under the hero.

.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);
}

03Speaker grid

Six cards tilting into place

Six cards fade and tilt into place one after another on load, each settling from a slight rotation back to flat.

Speaker grid
Open in a new tab

03 · Speaker grid — Measured area 40.452% · moving frames 13/23Original by GODRICH · SCSS and React ship in the zip · See the password

staggergrid-template-columnsclick

One bio at a time

Clicking a card expands only that card's one-line bio with a max-height transition while the other five stay closed, which suits a speaker lineup, a team page, or a panel of judges.

.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; }

04Venue block

Three real tab buttons

Three real role="tab" buttons for subway, bus, and driving control which directions paragraph is visible, sliding an underline indicator with translateX and cross-fading the panel text with opacity.

Venue block
Open in a new tab

04 · Venue block — Measured area 2.01% · moving frames 20/23Original by GODRICH · SCSS and React ship in the zip · See the password

opacityaria-selectedclick

A pin that keeps rippling

A pin on the map placeholder keeps rippling in the background the whole time, which is exactly the shape a "getting here" section or a store-locator card needs.

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);
    });
  });
});

05Ticket pricing

A glow that never stops

The middle card carries a "popular" badge and a looping box-shadow glow that never stops, while the early-bird/regular role="switch" at the top cross-fades all three price figures at once with opacity the instant it's toggled.

Ticket pricing
Open in a new tab

05 · Ticket pricing — Measured area 4.375% · moving frames 8/23Original by GODRICH · SCSS and React ship in the zip · See the password

scaleopacityaria-checked

An instant price toggle

This is built for a ticket sales page or any tiered pricing comparison where the toggle needs to feel instant rather than reloaded.

.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; }

06Sponsor logo bar

A track duplicated end to end

Eight placeholder logo blocks — shapes, not real logos — sit in a track duplicated end to end so a translateX(-50%) loop scrolls forever with no visible seam, and hovering or tapping the pause button stops it in place.

Sponsor logo bar
Open in a new tab

06 · Sponsor logo bar — Measured area 8.057% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · See the password

translateX(-50%)animation-play-statepointerenter

Pausing in place

It's the standard spot for a sponsor row, a partner list, or a "featured in" media bar.

// the track itself skips gap — each logo gets margin-right instead
.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%); }
}

07Event FAQ

Only one question open

Only one of the three questions can be open at a time — clicking a question expands its answer with a grid-template-rows: 0fr → 1fr transition and rotates the chevron, and opening one closes whichever was open before it.

Event FAQ
Open in a new tab

07 · Event FAQ — Measured area 28.795% · moving frames 10/23Original by GODRICH · SCSS and React ship in the zip · See the password

grid-template-rowsaria-expandedclick

Why the page never grows tall

Ticket, refund, and parking questions are exactly what this single-open accordion pattern is built to answer without the page growing tall. For a page that leads with finished work rather than an event, the project showcase section templates are that arrangement.

.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; }

08Register CTA

An event registration landing page template that really validates

Submitting the form runs a real regex check on the email address rather than pretending to validate.

Register CTA
Open in a new tab

08 · Register CTA — Measured area 0.857% · moving frames 9/23Original by GODRICH · SCSS and React ship in the zip · See the password

stroke-dashoffsetopacitysubmit

Pass, fail, and what moves

A passing address draws a checkmark onto the button with stroke-dashoffset and swaps the label to "done," while a failing one shakes the input left and right with translateX — nothing about the input itself moves before that submit happens.

form.addEventListener('submit', function (e) {
  e.preventDefault();
  var ok = /^\S+@\S+\.\S+$/.test(input.value);
  if (ok) {
    form.classList.add('is-done');
    done.textContent = 'Registered — check your inbox for confirmation.';
  } else {
    input.classList.remove('is-shake');
    void input.offsetWidth;
    input.classList.add('is-shake');
    done.textContent = 'Enter a valid email address.';
  }
});

09Countdown band

Counting to a real deadline

Four number cards count down to a real deadline set exactly 7 days, 4 hours, 20 minutes, and 15 seconds from the moment the page loads, updating every second through setInterval.

Countdown band
Open in a new tab

09 · Countdown band — Measured area 16.861% · moving frames 12/23Original by GODRICH · SCSS and React ship in the zip · See the password

translateYsetIntervalDate

Why the seconds card nudges

The seconds card nudges up and scales slightly on every tick rather than flipping — a deliberate choice covered in the trap below — which makes it a good fit for a pre-event countdown band or an early-bird deadline banner.

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);

Where it breaks — the trap

The 320x200 breakpoint

The real trap across all nine wasn't any one animation — it was the 320×200 breakpoint. At the full 480×300 canvas every section behaved, but five of the nine (speaker grid, venue block, ticket pricing, FAQ, register CTA) grew to 260–450px tall once the width dropped to 320px, blowing past the 200px height budget. Folding the speaker grid to two columns made it worse, not better: six cards at two per row become three rows, which is taller than three cards per row ever was. The fix that actually worked was keeping three columns everywhere and hiding or shrinking only secondary text — role labels, venue descriptions, ticket line items — inside a @media (max-width: 340px), (max-height: 220px) block.

A flip that went flat

The countdown seconds card also went through a real rewrite. The first version flipped it with rotateX(0deg → -90deg → 0deg) for a coin-flip effect, but without a perspective value on the parent, the card went completely flat exactly at the 90-degree midpoint — MDN's transform-style reference explains why a 3D rotation collapses to nothing without a perspective context, and a poster screenshot landing on that frame showed a flat dash instead of a digit. Swapping to a translateY + scale nudge removed the flat frame entirely while keeping a visible tick. A countdown left standing past its date is how any time-bound part ages — the dashboard components go stale the same way when no refresh cadence is set. The sponsor logo bar has its own version of this problem: give the duplicated track a gap instead of margin-right on each logo and the seam between the two copies comes up short by gap ÷ 2, so the infinite scroll visibly jumps once per loop. With both fixes in and the 320×200 breakpoint sorted, open the zip with 5ngtcz6h typed exactly as it appears here and all nine behave the way this page describes.

Accessibility

State survives reduced motion

Every one of the nine respects prefers-reduced-motion: reduce, but each keeps its actual state rather than just going still — the schedule fill stays at whatever point it reached, the FAQ answer stays open once expanded, and the countdown digits keep updating every second while only the seconds card's nudge and the background glow turn off.

Tabs built to the ARIA pattern

The venue block (04) is a real role="tablist" with role="tab" and live aria-selected, built to the keyboard behavior in the W3C ARIA Authoring Practices tabs pattern.

Status and timer regions

The register CTA's result text (08) sits in a role="status" region so a screen reader announces success or failure the moment the form submits, and the countdown (09) carries role="timer" with aria-live="off" so the once-a-second digit change never interrupts whatever else is being read. More templates and layout sections live on the template category hub, and what this site does and doesn't sell is on the about page.

FAQ

Can I drop real photos and a logo into these event landing page examples?

Yes — that's the point. Every photo, avatar, and logo across all nine is a CSS shape built from this site's own tokens, not a real image file, so swapping in an actual <img> or a brand's logo file is a straight replacement that doesn't touch the layout or the JavaScript. The color tokens in demo-tokens.scss can stay as they are or get swapped for a brand palette without breaking anything.

Does the countdown band actually count down, or is it just decoration?

It actually counts down. The target time is set to exactly 7 days, 4 hours, 20 minutes, and 15 seconds past the moment the page loads, and setInterval recalculates the remaining days, hours, minutes, and seconds every second from Date.now(). Swapping in a real event date is a one-line change to that target value — the React port takes the same number as a targetMs prop.

How different is the React version from the plain CSS one?

The behavior is identical. Active tab, open accordion item, and the toggled switch all live in useState instead of DOM attributes, and the three SCSS variables — duration, easing, color — come in as component props that get forwarded to CSS custom properties. The countdown component runs the same setInterval tick logic inside a useEffect with its own cleanup, so the two versions tick in lockstep.

Enter the archive password

The password is inside this article. You will find it as you read.