Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/app/events/[world]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export default async function WorldPage({ params }: PageProps<'/events/[world]'>
<p className={styles.lead}>{w.intro}</p>
<div className={styles.actions}>
<a href={`/#/world/${w.slug}`} className={`${styles.cta} ${styles.ctaPrimary}`}>ENTER THE WORLD</a>
<Link href="/events" className={`${styles.cta} ${styles.ctaGhost}`}>ALL EVENTS</Link>
<Link prefetch={false} href="/events" className={`${styles.cta} ${styles.ctaGhost}`}>ALL EVENTS</Link>
</div>
</section>

Expand Down Expand Up @@ -123,7 +123,7 @@ export default async function WorldPage({ params }: PageProps<'/events/[world]'>

<section className={styles.section} aria-label="Next world">
<p className={styles.kicker}>NEXT WORLD · {next.statL.toUpperCase()}</p>
<p className={styles.h2} style={{ '--accent': next.accentL } as CSSProperties}><Link href={`/events/${next.slug}`}>{next.name} →</Link></p>
<p className={styles.h2} style={{ '--accent': next.accentL } as CSSProperties}><Link prefetch={false} href={`/events/${next.slug}`}>{next.name} →</Link></p>
</section>
</div>
</EventsChrome>
Expand Down
6 changes: 3 additions & 3 deletions src/app/events/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default async function EventsPage() {
</p>
<div className={styles.actions}>
<a href={`/#/worlds/${WORLDS[0].slug}`} className={`${styles.cta} ${styles.ctaPrimary}`}>EXPLORE THE WORLDS</a>
<Link href="/" className={`${styles.cta} ${styles.ctaGhost}`}>BACK TO INNOVISION</Link>
<Link prefetch={false} href="/" className={`${styles.cta} ${styles.ctaGhost}`}>BACK TO INNOVISION</Link>
</div>
</section>

Expand All @@ -59,7 +59,7 @@ export default async function EventsPage() {
{worlds.map(({ w, list }) => (
<article key={w.slug} className={styles.world} style={{ '--accent': w.accentL } as CSSProperties}>
<p className={styles.kicker} style={{ margin: 0, color: w.accentL }}>{w.statL.toUpperCase()} · {w.category.toUpperCase()}</p>
<h3 className={styles.h2}><Link href={`/events/${w.slug}`}>{w.name}</Link></h3>
<h3 className={styles.h2}><Link prefetch={false} href={`/events/${w.slug}`}>{w.name}</Link></h3>
<p className={styles.tagline} style={{ margin: 0 }}>{w.tagline}</p>
<p className={styles.worldIntro}>{w.intro}</p>
{list.length > 0 ? (
Expand All @@ -73,7 +73,7 @@ export default async function EventsPage() {
<p className={styles.count}>LINE-UP ANNOUNCED SOON</p>
)}
<div className={styles.links}>
<Link href={`/events/${w.slug}`} className={styles.link}>{w.name.toUpperCase()} →</Link>
<Link prefetch={false} href={`/events/${w.slug}`} className={styles.link}>{w.name.toUpperCase()} →</Link>
<a href={`/#/world/${w.slug}`} className={styles.link}>ENTER THE WORLD</a>
</div>
</article>
Expand Down
20 changes: 11 additions & 9 deletions src/components/events/EventsChrome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import styles from './EventsPage.module.css';
* BreadcrumbList JSON-LD describes, and a footer linking every world page. Server-rendered, no client JS.
* Links into the animated explorer (/#/...) are plain <a>, not <Link>: the explorer reads its hash route once its
* loader finishes, and a full page load is what keeps that hash (as for a visitor arriving from search).
* Every <Link> here (and on the /events pages) has prefetch={false}: by default each one in the viewport costs uncached
* RSC requests on Vercel (3 to 5 per page view were measured). A click still navigates client-side, fetching then.
*
* @param trail [name, path] from the home page down to this page; the last entry is the current page.
*/
Expand All @@ -19,15 +21,15 @@ export default function EventsChrome({ trail, children }: { trail: [string, stri
<img className={styles.stars} src="/assets/starfield.svg" alt="" decoding="async" />
<div className={styles.wrap}>
<header className={styles.top}>
<Link href="/" className={styles.brand} aria-label="INNOVISION 2026 home">
<Link prefetch={false} href="/" className={styles.brand} aria-label="INNOVISION 2026 home">
<Logo style={{ width: '22px' }} />
INNOVISION
</Link>
<nav aria-label="Breadcrumb" className={styles.crumbs}>
<ol>
{trail.map(([name, path], i) => (
<li key={path}>
{i === trail.length - 1 ? <span aria-current="page">{name}</span> : <Link href={path}>{name}</Link>}
{i === trail.length - 1 ? <span aria-current="page">{name}</span> : <Link prefetch={false} href={path}>{name}</Link>}
</li>
))}
</ol>
Expand All @@ -38,19 +40,19 @@ export default function EventsChrome({ trail, children }: { trail: [string, stri
<nav aria-label="Event worlds">
<p>WORLDS</p>
<ul>
<li><Link href="/events">All events</Link></li>
{WORLDS.map((w) => <li key={w.slug}><Link href={`/events/${w.slug}`}>{w.name}</Link></li>)}
<li><Link prefetch={false} href="/events">All events</Link></li>
{WORLDS.map((w) => <li key={w.slug}><Link prefetch={false} href={`/events/${w.slug}`}>{w.name}</Link></li>)}
</ul>
</nav>
<nav aria-label="Innovision">
<p>INNOVISION</p>
<ul>
<li><Link href="/">Home</Link></li>
<li><Link prefetch={false} href="/">Home</Link></li>
<li><a href={`/#/worlds/${WORLDS[0].slug}`}>Explore the worlds</a></li>
<li><Link href="/register">Registrations</Link></li>
<li><Link href="/merch">Official merch</Link></li>
<li><Link href="/privacy">Privacy Policy</Link></li>
<li><Link href="/terms">Terms and Conditions</Link></li>
<li><Link prefetch={false} href="/register">Registrations</Link></li>
<li><Link prefetch={false} href="/merch">Official merch</Link></li>
<li><Link prefetch={false} href="/privacy">Privacy Policy</Link></li>
<li><Link prefetch={false} href="/terms">Terms and Conditions</Link></li>
</ul>
</nav>
<p style={{ margin: 0, alignSelf: 'flex-end' }}>© 2026 Innovision · NIT Rourkela, Odisha</p>
Expand Down
12 changes: 8 additions & 4 deletions src/components/innovision/Innovision.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -728,8 +728,10 @@ export class Innovision extends Component<Props, State> {
this._warmed = true;
if (slowNet()) return;
// The overlay and view a visitor is most likely to open next are fetched ahead too (their own hover/tap also does).
// The pages of their own too, so MERCH (and REGISTER while closed) open at once (a no-op on the dev server).
this.cleanups.push(this.idle(() => { this.prefetch('auth'); this.prefetch('detail'); OWN_PAGES.forEach((p) => this.props.router?.prefetch(p)); }));
// The pages of their own (OWN_PAGES: MERCH, REGISTER while closed) are NOT prefetched: each router.prefetch cost 3
// uncached RSC requests on every home page view, for a page few visitors open. A click on one still switches
// client-side (the document click listener in componentDidMount), fetching the route then.
this.cleanups.push(this.idle(() => { this.prefetch('auth'); this.prefetch('detail'); }));
// The menu (where the top bar has a MENU button) and the register / log-in overlay are mounted, closed, ahead of
// time: mounting one on its first tap blocked a phone for ~100 ms just as its reveal began. One per idle period.
this.cleanups.push(this.idle(() => {
Expand Down Expand Up @@ -2947,8 +2949,10 @@ export class Innovision extends Component<Props, State> {
authSub: reg
? 'Three short stops to register for Innovision 2026 at NIT Rourkela.'
: am === 'login' ? 'Sign in or create your account with Google.' : P.status === 'CONFIRMED' ? 'Your payment is verified. Your INNO pass is always available here.' : P.status === 'REJECTED' ? (this.canRegisterAgain() ? 'See why below, then register again with the right details.' : 'Please contact the registration helpdesk for assistance.') : 'Your payment is under review by the registration team. Your pass appears here after approval.',
// The seat counter (GET /api/seats) beside the registration form.
showSeats: reg,
// The seat counter (GET /api/seats) beside the registration form. Only while the overlay is open: it is mounted,
// closed, at idle (warmDeferred), and a mounted counter polls every 30 s. `auth` turns true before the overlay
// animates in and false after it has animated out, so the counter is never cut off mid-animation.
showSeats: reg && s.auth,
// REGISTER AGAIN on a rejected pass; the pass itself decides whether to show it (attempts left).
registerAgain: REGISTRATIONS_OPEN ? this.registerAgain : undefined,
idCardUploaded: this.idCardUploaded,
Expand Down
Loading