This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ before writing any code. Heed deprecation notices.
What this is: The coming-soon / landing page for CUSEC 2027 (26th annual Canadian University Software Engineering Conference, Montréal QC, January 2027). Currently a single-page splash with a draggable Win95-style UI window, locale switcher, and animated background.
Live domain: https://2027.cusec.net
| Layer | Technology |
|---|---|
| Framework | Next.js 16.2.6 (App Router, Turbopack) |
| Language | TypeScript 5 |
| Styling | Plain CSS modules + Tailwind v4 (minimal use) |
| i18n | next-intl 4.9.0 |
| Icons | lucide-react |
| React | 19.2.4 |
| Font | Retropix (custom woff2, pixel-art style) + MS Sans Serif fallback |
Next.js 16 renamed the middleware file convention. Never create src/middleware.ts — it will conflict and break routing.
src/proxy.ts ← the correct file (already set up with next-intl)
Locales are detected from the Accept-Language header / cookie, not from URL path segments. The URL never shows /en-CA/ or /fr-CA/. All navigation uses wrappers from src/i18n/navigation.ts, never Next.js's next/navigation directly.
src/app/
layout.tsx ← root layout (metadata, viewport, global CSS imports)
globals.css ← Tailwind import + base resets
manifest.ts ← Web App Manifest (MetadataRoute.Manifest)
page.tsx ← redirected by proxy — effectively unused
[locale]/
layout.tsx ← locale-aware layout: NextIntlClientProvider + Navbar + SplashPage
page.tsx ← placeholder (real content is in SplashPage, rendered by locale layout)
The [locale] segment is internal routing only — it never appears in the browser URL bar.
| File | Purpose |
|---|---|
src/app/components/SplashPage/SplashPage.tsx |
Server component. Composes the full splash page: wavy animated title, CUSEC logo, SplashPageUI, and the waveform video background. |
src/app/components/SplashPage/SplashPageUI.tsx |
Client component ("use client"). The draggable Win95-style application window. Contains all pointer event drag logic (bounds-clamped, auto-returns to origin after 1s idle). |
src/app/components/Navbar/Navbar.tsx |
Fixed top navbar shell. Currently just renders LocaleSwitcher. |
src/app/components/Navbar/LocaleSwitcher.tsx |
Client component. Globe icon + <select> for en-CA / fr-CA. Uses useRouter / usePathname from src/i18n/navigation.ts. |
All CSS is plain files, no CSS Modules. Imported globally via src/app/styles/index.css.
| File | Covers |
|---|---|
src/app/globals.css |
Tailwind import, color-scheme: light, base body background |
src/app/styles/index.css |
Imports SplashPage.css + UIWindow.css; patches splash-wrapper top padding to account for fixed navbar height + safe-area inset |
src/app/styles/Splash/SplashPage.css |
.splash-wrapper, .main-splash-content, .splash-title, .title-row, .splash-logo, .splash-ui-wrapper, waveform video, responsive breakpoints |
src/app/styles/Splash/UIWindow.css |
Everything inside SplashPageUI: nav bar, window buttons, item icons, CTA area, sponsorship button. Has breakpoints at 1400px, 640px, 370px. |
src/app/styles/navbar.css |
.Navbar, .Navbar-locale-switcher, .Navbar-locale-icon, .Navbar-locale-select-wrapper, .Navbar-locale-select |
| File | Purpose |
|---|---|
src/i18n/routing.ts |
Defines locales ['en-CA', 'fr-CA'], defaultLocale: 'en-CA', localePrefix: 'never' |
src/i18n/request.ts |
Server-side locale resolution from request; loads messages/<locale>.json |
src/i18n/navigation.ts |
Re-exports Link, redirect, usePathname, useRouter, getPathname from next-intl — always use these instead of next/navigation |
src/proxy.ts |
next-intl middleware. Handles locale detection and cookie. Matches all paths except api, _next, _vercel, and files with a dot. |
messages/en-CA.json |
English strings |
messages/fr-CA.json |
French strings |
Message namespaces in use:
SplashPage.*— title, edition-link, attendance-*, sponsorship-interestHomePage.*— placeholder, not currently rendered
Static assets live in public/assets/, not in src/. Reference them as /assets/filename.ext.
| File | Dimensions | Usage |
|---|---|---|
public/assets/navigation_ui_window.png |
735×514 | Desktop UI window background |
public/assets/navigation_ui_window_skinny.png |
351×576 | Mobile UI window background (shown ≤640px) |
public/assets/splash_bg.webp |
1448×1086 | Full-page background image |
public/assets/calendar.webp |
83×83 | "Jan. 2027" icon in the window |
public/assets/globe.webp |
89×89 | "Montreal, QC" icon in the window |
public/assets/cusec_aero_logo.webp |
146×146 | Logo beside "2027" title |
public/splash_waveform.webm |
— | Animated waveform overlay (screen blend mode for iOS VP9 alpha fix) |
public/logo_animated.webm |
— | Animated logo (currently commented out, replaced by static PNG) |
public/cusec-logo.png |
— | OG image + PWA icon (should be 1200×630 for best OG) |
public/favicon.ico / favicon-16x16.png / favicon-32x32.png |
— | Favicons |
SVG components (code, not static assets) live in src/app/assets/FigmaSVGs.tsx:
FileFolder— pixel-art folder icon for "25th Edition" linkSponsorshipInterestButton— Win95-style button SVG,viewBox="0 0 616 75"(8.2:1 aspect ratio — render at full width, do not scale height or it distorts)
The SVG file also exports splitCtaLabel, a utility that word-wraps long button label strings to at most 2 lines.
src/fonts/retropix.woff2 — loaded via @font-face in SplashPage.css as "Retropix". Used for all monospace/pixel-art text (CTA buttons, email input, etc.).
Color palette (key values):
#103436— dark teal, primary brand colour (text, outlines, theme-color)#ffffff— highlights / borders#2b2b2b— shadows / borders#bfbfbf/#c0c0c0— Win95 grey (buttons)#1A66D4— locale icon blue
Win95 border pattern (used on all "window" UI elements):
border-top: 2px solid #ffffff;
border-left: 2px solid #ffffff;
border-right: 2px solid #2b2b2b;
border-bottom: 2px solid #2b2b2b;
box-shadow: inset 1px 1px 0 #dbdbdb;Do not reduce these to 1px on mobile — they become invisible.
Responsive breakpoints:
≤1400px— reduce icon sizes slightly≤980px— stack layout to single column≤640px— mobile layout: skinny window image, row-layout items, CTA below window≤370px— very small phones, smaller nav title
SplashPageUI implements a pointer-capture drag for the window:
- Drag handle:
.UI-navbar (top of the window). Clicking a<button>inside the nav does NOT initiate drag. - Bounds: Clamped to
.splash-wrapperon everypointerdownso the window can't be dragged outside the page. - Auto-return: If the cursor leaves the window and stays absent for 1 second, the window slides back to
translate(0, 0)with a spring animation (cubic-bezier(0.34, 1.4, 0.64, 1)). - Float animation:
.splash-ui-wrapperhas a CSSsplash-ui-hoverkeyframe animation. It pauses while dragging via:has(.UI-nav--dragging). - State:
offsetRefis the source of truth during drag (avoids React re-render lag);offsetstate drives thestyleprop for the actual transform.
- Defined in
src/app/layout.tsx(root, language-agnostic defaults) andsrc/app/[locale]/layout.tsx(per-locale overrides for title/description/OG). - Web App Manifest at
src/app/manifest.ts(Next.js built-inMetadataRoute.Manifest). - Google Search Console verification token field is empty in
layout.tsx— fill in when needed. - OG image is
/cusec-logo.png— should be replaced with a proper 1200×630 image.
- Never use
next/navigationdirectly. Always import from@/i18n/navigationfor locale-aware routing. - Never create
src/middleware.ts— usesrc/proxy.ts. - Static assets belong in
public/, notsrc/app/assets/. Only code (TSX/TS) goes insrc/. - The waveform video uses
mix-blend-mode: screen— this is intentional. iOS Safari drops VP9 alpha channel, leaving black; screen blend makes black transparent without needing a separate codec. - The
SponsorshipInterestButtonSVG is 8.2:1 wide. Render it withwidth: 100%and let it scale proportionally. Do not applyscaleYor set a fixed height — it will distort. src/app/page.tsxexists but is essentially unused. The locale layout ([locale]/layout.tsx) handles rendering. The rootpage.tsxis a remnant — don't put real content there.- The
HomePagemessage namespace is a placeholder and not rendered anywhere visible. Real content usesSplashPage.*.