From 01351413e1582f8c417348af96b9f7eb8cd58bcb Mon Sep 17 00:00:00 2001 From: Krish Agarwal Date: Tue, 13 Oct 2026 13:03:55 +0530 Subject: [PATCH] gallery responsive fixes --- src/app/globals.css | 29 +++++++++++++++++------- src/components/innovision/DetailView.tsx | 23 +++++++++++++++---- src/components/innovision/HomeView.tsx | 18 +++++++++------ src/components/innovision/Innovision.tsx | 22 ++++++++++++++++-- src/components/innovision/data.ts | 3 ++- 5 files changed, 72 insertions(+), 23 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 249a222..683cac0 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -392,22 +392,35 @@ body::after{content:'';position:fixed;inset:0;pointer-events:none;z-index:9999;o [data-lowpower] [data-lp-skip]{display:none} /* ---------- home gallery tunnel (HomeView.tsx) ---------- */ +/* The visible height, so the counter and "View full gallery" aren't tucked behind a phone's browser toolbar. */ +.t-stage{height:100vh;height:100dvh} .t-scrim{display:none} -/* Phones: photos are capped by height too (--ar is the photo's width/height; --tx/--ty its offset from the centre), so a tall one can't outgrow the room - between the heading and the counter; the tunnel's centre and vanishing point drop into that room; and dark fades sit - behind the heading and the counter, so a photo passing beneath never covers the text. */ +/* Narrow phones: a smaller counter and button keep the bottom bar on one line. */ +@media (max-width:400px){ + .t-bar{gap:10px !important} + .t-bar [data-t-count]{font-size:32px !important} + .t-bar a{padding:12px 16px !important;font-size:12px !important} +} +/* Phones: photos are capped by height too (--ar is the photo's width/height; --tx/--ty its offset from the centre), so a tall + one can't outgrow the room between the nav and the counter. The heading fades out as the tunnel starts, so the photos + fly through the middle of the screen; dark fades sit behind the heading and the counter, so a photo passing beneath + never covers the text. */ @media (max-width:719px){ - .t-space{perspective-origin:50% 56% !important} - .t-frame{width:min(54vw,calc(28vh * var(--ar))) !important;top:calc(56% + var(--ty) * .5) !important} + .t-space{perspective-origin:50% 50% !important} + .t-frame{width:min(70vw,calc(36vh * var(--ar))) !important;top:calc(50% + var(--ty) * .5) !important} .t-scrim{display:block;position:absolute;left:0;right:0;pointer-events:none} .t-scrim-top{top:0;height:calc(4vh + 290px);background:linear-gradient(180deg,#141312 0,rgba(20,19,18,.94) 62%,rgba(20,19,18,0))} .t-scrim-bot{bottom:0;height:200px;background:linear-gradient(0deg,#141312 0,rgba(20,19,18,.9) 45%,rgba(20,19,18,0))} } +/* Portrait tablets: the desktop width (30vw) leaves the photos small in a tall screen, so size them like phones do. */ +@media (min-width:720px) and (max-width:1199px) and (orientation:portrait){ + .t-frame{width:min(56vw,calc(34vh * var(--ar))) !important} +} /* Landscape phones: too short to stack the photos between the heading and the counter, so they fly through the open - right-hand side instead, sized off the height. */ + right-hand side instead, sized off the height so the closest one still fits between the nav and "View full gallery". */ @media (max-height:499px) and (orientation:landscape){ - .t-space{perspective-origin:76% 42% !important} - .t-frame{width:calc(38vh * var(--ar)) !important;left:calc(76% + var(--tx) * .3) !important;top:calc(42% + var(--ty) * .3) !important} + .t-space{perspective-origin:76% 40% !important} + .t-frame{width:calc(30vh * var(--ar)) !important;left:calc(76% + var(--tx) * .3) !important;top:calc(40% + var(--ty) * .3) !important} } /* ---------- gallery (GalleryView.tsx) ---------- */ diff --git a/src/components/innovision/DetailView.tsx b/src/components/innovision/DetailView.tsx index 256b483..f73744f 100644 --- a/src/components/innovision/DetailView.tsx +++ b/src/components/innovision/DetailView.tsx @@ -446,10 +446,15 @@ export default function DetailView({ v }: { v: V }) {

Mission manifest

-
- {cw.missionCount} - EVENTSON BOARD -
+ {/* Until a world has published events, its count reads as "coming soon" rather than "00". */} + {cw.missions.length ? ( +
+ {cw.missionCount} + EVENTSON BOARD +
+ ) : ( + LINE-UPCOMING SOON + )} {/* One block of tickets per group: a world that combines categories (DTS and Fun) titles each one. */} {cw.groups.map((g, gI) => ( @@ -459,10 +464,18 @@ export default function DetailView({ v }: { v: V }) {

{g.titleU}

- {g.count} {g.missions.length === 1 ? "EVENT" : "EVENTS"} + {g.soon ? "COMING SOON" : `${g.count} ${g.missions.length === 1 ? "EVENT" : "EVENTS"}`} )}
+ {/* A group whose events aren't published yet holds one "Coming soon" ticket stub in their place. */} + {g.soon && ( +
+ {g.titleU} · {cw.serial} + Coming soon +

The {g.title} line-up is still being charted. Check back soon for the full manifest.

+
+ )} {g.missions.map((m) => (
diff --git a/src/components/innovision/HomeView.tsx b/src/components/innovision/HomeView.tsx index d7fd2b6..ca8d194 100644 --- a/src/components/innovision/HomeView.tsx +++ b/src/components/innovision/HomeView.tsx @@ -177,7 +177,7 @@ export default function HomeView({ v }: { v: V }) {
-
+
@@ -199,21 +199,25 @@ export default function HomeView({ v }: { v: V }) {
{/* Phones only (globals.css): dark fades behind the heading and the counter, so a photo flying past never covers the text. */} - + {/* The heading and its scrim fade out as the visitor scrolls into the tunnel (Innovision#homeScroll). */} + -
+

GALLERY

Into the archive

Keep scrolling to fly through moments from Innovision past.

-
+
01 / {v.tunnelTotal}
- - VIEW FULL GALLERY - + {/* Shown only at the end of the tunnel (Innovision#homeScroll); hidden until then, so it isn't tabbable either. */} +
diff --git a/src/components/innovision/Innovision.tsx b/src/components/innovision/Innovision.tsx index 6ac650e..9b84f07 100644 --- a/src/components/innovision/Innovision.tsx +++ b/src/components/innovision/Innovision.tsx @@ -1002,9 +1002,24 @@ export class Innovision extends Component { const tun = this.$('[data-tunnel]'); if (tun) { const fr = this.$$('[data-t-frame]'), N = fr.length, TGAP = 1000, cnt = this.$('[data-t-count]'); + const head = this.$$('[data-t-head]'), cta = this.$('[data-t-cta]'); fr.forEach((f, i) => { f.style.zIndex = String(N - i); }); + // The heading (and the phone scrim behind it) fades out over the first steps into the tunnel; "View full gallery" + // fades in once the last photo is on its way. Both follow the scroll, so scrolling back up reverses them. + const fade = (el: HTMLElement, o: number, dy: number) => { + const os = String(+o.toFixed(3)), vis = o > 0 ? 'visible' : 'hidden'; + if (el.style.opacity !== os) { el.style.opacity = os; el.style.transform = 'translateY(' + ((1 - o) * dy).toFixed(1) + 'px)'; } + if (el.style.visibility !== vis) el.style.visibility = vis; + }; // Only write what changed: replacing the counter's text every scroll update invalidates layout. const upd = (p: number) => { + const ho = Math.max(0, 1 - p / .05); + head.forEach((el) => fade(el, ho, -24)); + if (cta) { + const co = Math.min(1, Math.max(0, (p - .86) / .08)); + fade(cta, co, 18); + cta.style.pointerEvents = co > .6 ? 'auto' : 'none'; + } const travel = p * (N * TGAP - 200); // Phones fade a photo out sooner as it passes the camera, before it grows past the room between the heading and the counter. const near = innerWidth < 720 || (innerHeight < 500 && innerWidth > innerHeight) ? 120 : 250; @@ -2670,11 +2685,14 @@ export class Innovision extends Component { { key: 'other', title: '', items: list.filter((m) => !dw.groups!.some(([cat]) => cat === m.cat)) }] : [{ key: 'all', title: '', items: list }]; // Numbered in display order; idx is the ticket's place in missions, which the event popup reads. + // A titled group with no events yet stays, shown as "Coming soon". let n = 0; - const groups = sections.filter((g) => g.items.length).map((g) => ({ + const groups = sections.filter((g) => g.items.length || g.title).map((g) => ({ key: g.key, + title: g.title, titleU: g.title.toUpperCase(), count: String(g.items.length).padStart(2, '0'), + soon: !g.items.length, missions: g.items.map((m) => { const k = n++; return { @@ -2697,7 +2715,7 @@ export class Innovision extends Component { serial: 'IV26-0' + (s.dIndex + 1), stampText: 'BOARDING SOON · ' + dw.statL.toUpperCase() + ' · ', frame: 'color-mix(in oklab, ' + dw.ink + ' 50%, transparent)', - ticker: [0, 1, 2, 3].flatMap(() => ['Now boarding · ' + dw.name, ...missions.map((m) => m.name)]).map((t) => ({ t })), + ticker: [0, 1, 2, 3].flatMap(() => ['Now boarding · ' + dw.name, ...groups.flatMap((g) => g.soon ? [g.titleU + ' · Coming soon'] : g.missions.map((m) => m.name))]).map((t) => ({ t })), chars: [...dw.name.toUpperCase()].map((ch) => ({ ch: ch === ' ' ? ' ' : ch })), words: dw.tagline.split(' ').map((t) => ({ t })), specs: dw.specs.map(([k, v], j) => ({ k, v, i: String(j + 1).padStart(2, '0') })), diff --git a/src/components/innovision/data.ts b/src/components/innovision/data.ts index 901db0c..074d656 100644 --- a/src/components/innovision/data.ts +++ b/src/components/innovision/data.ts @@ -106,7 +106,8 @@ export const WORLDS: World[] = [ specs: [['Category', 'Fun & games'], ['Format', 'Open to all'], ['Vibe', 'Euphoric'], ['Fuel', 'Team spirit'], ['Status', 'Boarding soon']], categories: ['dts events', 'fun events'], groups: [['dts events', 'DTS Events'], ['fun events', 'Fun Events']], - missions: [['Quiz Night', 'Tech, pop culture and everything in between.', 'Team · 2–3', '3 rounds', 'dts events'], ['Project Expo', 'Walk the hall of student builds and vote for your favourite.', 'Open to all', 'All fest', 'dts events'], ['Gaming Arena', 'Squad up for LAN battles and console showdowns.', 'Squad', 'All night', 'fun events'], ['Treasure Hunt', 'Clues scattered across campus. Only the sharpest crews finish.', 'Team · 3–4', 'Campus-wide', 'fun events']] }, + // No placeholder line-up: each group shows a "Coming soon" card until its events are published. + missions: [] }, ]; /**