feat(events): surface the buy-ticket CTA above the description + sticky bar #149

Merged
padreug merged 1 commit from feat/events-ticket-cta-placement into dev 2026-09-09 11:06:00 +00:00
Owner

Why

On events with a long description (Château du Faune: ~1.7k characters) the Buy ticket button sat at the very bottom of the page, below the fold on every phone. The buy action is the reason most visitors open the page.

What

  • The ticket block (owned-tickets banner, Buy ticket + availability, past / sold-out notices, and the host's Scan tickets CTA) moves from below the description to directly under the date and location.
  • A sticky bar — title, price, tickets left, the same CTA (or "View in My Tickets" when the event is sold out but the visitor holds tickets) — pins above the bottom nav (bottom: 3.5rem + safe-area) whenever the inline block is scrolled out of view (useElementVisibility from VueUse), and hides again when it is back or while the purchase / return dialogs are open. The page gets extra bottom padding while the bar is up so the organizer card is never covered.
  • Price / label strings factored into priceLabel / buyLabel so inline and sticky stay identical.

One file, src/modules/events/views/EventDetailPage.vue. vue-tsc -b clean, 26/26 events tests. Verified against the demo backend with a local dev server at 390×844 and 1280×900: CTA under date/location on load; sticky bar appears after scrolling past the block and disappears at the top; short-description events never show the bar (screenshots on the session).

🤖 Generated with Claude Code

https://claude.ai/code/session_01ByAwHU4pRnyE58YocQvAas

## Why On events with a long description (Château du Faune: ~1.7k characters) the Buy ticket button sat at the very bottom of the page, below the fold on every phone. The buy action is the reason most visitors open the page. ## What - The ticket block (owned-tickets banner, Buy ticket + availability, past / sold-out notices, and the host's Scan tickets CTA) moves from below the description to directly under the date and location. - A sticky bar — title, price, tickets left, the same CTA (or "View in My Tickets" when the event is sold out but the visitor holds tickets) — pins above the bottom nav (`bottom: 3.5rem + safe-area`) whenever the inline block is scrolled out of view (`useElementVisibility` from VueUse), and hides again when it is back or while the purchase / return dialogs are open. The page gets extra bottom padding while the bar is up so the organizer card is never covered. - Price / label strings factored into `priceLabel` / `buyLabel` so inline and sticky stay identical. One file, `src/modules/events/views/EventDetailPage.vue`. `vue-tsc -b` clean, 26/26 events tests. Verified against the demo backend with a local dev server at 390×844 and 1280×900: CTA under date/location on load; sticky bar appears after scrolling past the block and disappears at the top; short-description events never show the bar (screenshots on the session). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01ByAwHU4pRnyE58YocQvAas
The ticket block (owned banner, Buy ticket + availability, past/sold-out
notices, and the host's Scan CTA) moved from below the description to
directly under the date and location. A long description was pushing
the primary action below the fold — on the Château du Faune event the
button sat under ~1.7k characters of text.

A sticky bar (title, price, tickets left, the same CTA) pins above the
bottom nav whenever the inline block is scrolled out of view
(useElementVisibility), and hides again when it is back, or while the
purchase / return dialogs are open. Container gets extra bottom padding
while the bar is up so the organizer card is never covered.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ByAwHU4pRnyE58YocQvAas
padreug deleted branch feat/events-ticket-cta-placement 2026-09-09 11:06:00 +00:00
Author
Owner

Two follow-up commits after the first demo look showed a gap under the sticky bar on both phone and desktop:

  • feat(layout): publish the bottom nav's rendered height as --bottom-nav-height — BottomNav measures itself (ResizeObserver) and writes the variable on <html>; index.css declares the 0px default; the restaurant item page's add-to-cart bar (which hand-derived 3.5rem + safe-area and carried a comment about an earlier gap of its own) is migrated to it. One source of truth for "flush above the nav".
  • fix(events): pin the sticky buy bar flush above the nav — the bar lived inside the page's space-y-6 column, whose child margin also offsets a fixed box (that was the 24px), and it used the hand-derived offset. It now teleports to <body> (same as the shell's dialogs/toaster) and uses the variable. Headless measurement: bar bottom == nav top at 390px and 1280px.
Two follow-up commits after the first demo look showed a gap under the sticky bar on both phone and desktop: - `feat(layout): publish the bottom nav's rendered height as --bottom-nav-height` — BottomNav measures itself (ResizeObserver) and writes the variable on `<html>`; `index.css` declares the `0px` default; the restaurant item page's add-to-cart bar (which hand-derived `3.5rem + safe-area` and carried a comment about an earlier gap of its own) is migrated to it. One source of truth for "flush above the nav". - `fix(events): pin the sticky buy bar flush above the nav` — the bar lived inside the page's `space-y-6` column, whose child margin also offsets a fixed box (that was the 24px), and it used the hand-derived offset. It now teleports to `<body>` (same as the shell's dialogs/toaster) and uses the variable. Headless measurement: bar bottom == nav top at 390px and 1280px.
Sign in to join this conversation.
No description provided.