Merge pull request #97 from mcgill-ecsess/antoine/preview-seo-changes-and-query-optimization

Weekend overhaul: SEO changes, query optimization, landing page, animations
This commit is contained in:
Antoine Phan
2026-01-26 23:51:05 -05:00
committed by GitHub
15 changed files with 258 additions and 207 deletions

View File

@@ -6,21 +6,29 @@
/* Light shades - for backgrounds and cards */ /* Light shades - for backgrounds and cards */
--color-ecsess-50: #e8ffd9; --color-ecsess-50: #e8ffd9;
--color-ecsess-100: #cce7ba; --color-ecsess-100: #cce7ba;
--color-ecsess-150: #bae9a5;
--color-ecsess-200: #a9d0a0; --color-ecsess-200: #a9d0a0;
/* Mid-light shades - for borders and hover states */ /* Mid-light shades - for borders and hover states */
--color-ecsess-250: #9cc295;
--color-ecsess-300: #8fb98a; --color-ecsess-300: #8fb98a;
--color-ecsess-350: #7daa7a;
--color-ecsess-400: #6a9a6a; --color-ecsess-400: #6a9a6a;
/* Mid shades - for accents and interactive elements */ /* Mid shades - for accents and interactive elements */
--color-ecsess-450: #62925a;
--color-ecsess-500: #5a8b5a; --color-ecsess-500: #5a8b5a;
--color-ecsess-550: #4c7a4f;
--color-ecsess-600: #3f6a3f; --color-ecsess-600: #3f6a3f;
--color-ecsess-650: #306032;
/* Mid-dark shades - for text on light backgrounds */ /* Mid-dark shades - for text on light backgrounds */
--color-ecsess-700: #2d5a2d; --color-ecsess-700: #2f4d29;
--color-ecsess-750: #1c4a1e;
--color-ecsess-800: #0a3d2a; --color-ecsess-800: #0a3d2a;
/* Dark shades - for text and backgrounds */ /* Dark shades - for text and backgrounds */
--color-ecsess-850: #083525;
--color-ecsess-900: #062c20; --color-ecsess-900: #062c20;
--color-ecsess-950: #031c15; --color-ecsess-950: #031c15;
@@ -28,14 +36,14 @@
--color-ecsess-black: #1f1f1f; --color-ecsess-black: #1f1f1f;
--color-ecsess-black-hover: #161917; --color-ecsess-black-hover: #161917;
--animate-wiggle: wiggle 0.3s ease-in-out infinite; --animate-wiggle: wiggle 0.5s ease-in-out 1;
@keyframes wiggle { @keyframes wiggle {
0%, 0%,
100% { 100% {
transform: rotateY(-3deg); transform: rotateY(-2.4deg);
} }
50% { 50% {
transform: rotateY(3deg); transform: rotateY(2.4deg);
} }
} }
} }

View File

@@ -1,11 +1,12 @@
<script lang="ts"> <script lang="ts">
let { href, external = false, children } = $props(); let { href, external = false, children, class: className = '' } = $props();
</script> </script>
<a <a
{href} {href}
target={external ? '_blank' : undefined} target={external ? '_blank' : undefined}
rel={external ? 'noopener noreferrer' : undefined} rel={external ? 'noopener noreferrer' : undefined}
class={className}
> >
{@render children()} {@render children()}
</a> </a>

View File

@@ -4,6 +4,6 @@
let { value } = $props(); let { value } = $props();
</script> </script>
<div class="flex flex-col justify-center-safe"> <div class="typography flex flex-col justify-center-safe">
<PortableText {value} /> <PortableText {value} />
</div> </div>

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Globe, Instagram, Wrench, Zap, CodeXml, Podcast } from '@lucide/svelte'; import { Globe, Instagram, Wrench, Users, CodeXml, Cpu } from '@lucide/svelte';
// All icons from @lucide/svelte share the same component type; reuse one for typing // All icons from @lucide/svelte share the same component type; reuse one for typing
type IconComponent = typeof Wrench; type IconComponent = typeof Wrench;
@@ -17,16 +17,11 @@
{ {
name: 'Code.Jam()', name: 'Code.Jam()',
description: description:
"McGill Engineering's largest annual hackathon. A 48-hour programming competition where students create innovative solutions to real-world problems.", "McGill Engineering's largest annual hackathon, a 36-hour programming competition where students create innovative projects!",
website: 'https://codejam.mcgilleus.ca/', website: 'https://codejam.mcgilleus.ca/',
instagram: 'https://www.instagram.com/mcgillcodejam/', instagram: 'https://www.instagram.com/mcgillcodejam/',
icon: CodeXml, icon: CodeXml,
features: [ features: ['Biggest Hackathon in Engineering', 'Great prizes', 'Networking opportunities']
'Biggest Hackathon in Engineering',
'Great prizes',
'Cool swags and merch',
'Networking opportunities'
]
}, },
{ {
name: 'The Factory', name: 'The Factory',
@@ -35,7 +30,16 @@
website: 'https://factory.mcgilleus.ca/', website: 'https://factory.mcgilleus.ca/',
instagram: 'https://www.instagram.com/thefactory_mcgill/', instagram: 'https://www.instagram.com/thefactory_mcgill/',
icon: Wrench, icon: Wrench,
features: ['Student-run Lab Space', '3D Printing', 'Equipment Rental', 'Hardware Workshops'] features: ['Student-run Lab Space', '3D Printing', 'Hardware Workshops']
},
{
name: 'ECSESSBits',
description:
'First Year Council of the McGill Electrical, Computer, Software Engineering Student Society.',
website: '',
instagram: 'https://www.instagram.com/ecsessbits/',
icon: Users,
features: ['First Year Council', 'Fun Events', 'Study Sessions']
}, },
{ {
name: 'IEEE McGill', name: 'IEEE McGill',
@@ -43,49 +47,40 @@
'One of the largest IEEE student branches in Eastern Canada, offering professional development, networking, and industry connections.', 'One of the largest IEEE student branches in Eastern Canada, offering professional development, networking, and industry connections.',
website: 'https://ieee.mcgilleus.ca/', website: 'https://ieee.mcgilleus.ca/',
instagram: 'https://www.instagram.com/ieeemcgill/', instagram: 'https://www.instagram.com/ieeemcgill/',
icon: Zap, icon: Cpu,
features: [ features: ['Technical Talks', 'Arduino Workshops', 'Networking Events']
'Technical Talks',
'Arduino Workshops',
'IEEEXtreme Competition',
'Networking Events'
]
} }
]; ];
</script> </script>
<div class="container mx-auto px-4"> <div class="container mx-auto px-4">
<!-- Section Header --> <!-- Section Header -->
<div class="mb-12 text-center"> <div class="my-12 text-center">
<h2 id="affiliated-clubs-title" class="text-ecsess-100 mb-4 text-4xl font-bold md:text-5xl"> <h2 id="affiliated-clubs-title" class="text-ecsess-100 mb-2 text-4xl font-bold md:text-5xl">
Subcommittees & Affiliated Groups Subcommittees & Affiliated Groups
</h2> </h2>
<p class="text-ecsess-200 mx-auto max-w-2xl text-lg"> <p class="text-ecsess-200 mx-auto max-w-2xl text-base">
Explore opportunities to enhance your skills, build innovative projects, and connect with the Explore opportunities to enhance your skills, build innovative projects, and connect with the
engineering community through our subcommittees and affiliated groups. engineering community through our subcommittees and affiliated groups.
</p> </p>
</div> </div>
<!-- Clubs Grid --> <!-- Clubs Grid: 2x2 on large screens -->
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3"> <div class="grid grid-cols-1 gap-8 md:grid-cols-2">
{#each groups as group, i (group.name)} {#each groups as group, i (group.name)}
{@const Icon = group.icon} {@const Icon = group.icon}
<article <article
class="group bg-ecsess-950 shadow-ecsess-800 relative flex flex-col overflow-hidden rounded-2xl shadow-xl transition-all duration-300 hover:-translate-y-2 hover:shadow-2xl" class="bg-ecsess-950 border-ecsess-800 flex flex-col overflow-hidden rounded-lg border text-left"
aria-labelledby={`group-${i}-title`} aria-labelledby={`group-${i}-title`}
> >
<!-- Decorative gradient bar --> <div class="flex flex-1 flex-col p-7 md:p-8">
<div class="from-ecsess-400 via-ecsess-500 to-ecsess-600 h-2 bg-gradient-to-r"></div> <!-- Header: icon + name -->
<header class="mb-5 flex items-center justify-start gap-4">
<div class="flex flex-1 flex-col p-6">
<!-- Icon and Name -->
<div class="mb-4 flex items-center justify-between gap-4">
<div class="flex items-center gap-4">
<div <div
class="group-hover:bg-ecsess-500 bg-ecsess-800 flex h-14 w-14 items-center justify-center rounded-xl shadow-md transition-all duration-300 group-hover:scale-110" class="bg-ecsess-800 flex h-14 w-14 shrink-0 items-center justify-center rounded-xl"
> >
<Icon <Icon
class="text-ecsess-300 h-7 w-7 transition-colors group-hover:text-white" class="text-ecsess-300 size-7"
strokeWidth={2.5} strokeWidth={2.5}
aria-hidden="true" aria-hidden="true"
focusable="false" focusable="false"
@@ -94,63 +89,53 @@
<h3 id={`group-${i}-title`} class="text-ecsess-50 text-2xl font-bold"> <h3 id={`group-${i}-title`} class="text-ecsess-50 text-2xl font-bold">
{group.name} {group.name}
</h3> </h3>
</div> </header>
<!-- Action Buttons -->
<div class="flex gap-2"> <!-- Description -->
<a <p class="text-ecsess-200 mb-5 text-base leading-relaxed md:text-lg">
href={group.website} {group.description}
target="_blank" </p>
rel="noopener noreferrer external"
aria-label={`Visit ${group.name} website`} <!-- Features -->
class="bg-ecsess-800 hover:bg-ecsess-500 text-ecsess-100 hover:text-ecsess-50 flex h-10 w-10 items-center justify-center rounded-lg shadow-md transition-all hover:shadow-lg active:scale-95" <ul class="mb-5 list-none space-y-2 ps-0 text-base md:text-lg" role="list">
> {#each group.features as feature (feature)}
<Globe class="h-5 w-5" strokeWidth={2.5} aria-hidden="true" focusable="false" /> <li class="flex items-center gap-2">
</a> <span class="bg-ecsess-500 h-1.5 w-1.5 shrink-0 rounded-full" aria-hidden="true"
></span>
<span class="text-ecsess-100 font-medium">{feature}</span>
</li>
{/each}
</ul>
<!-- Links -->
<div class="border-ecsess-800 mt-auto flex flex-wrap items-center gap-3 border-t pt-5">
{#if group.instagram} {#if group.instagram}
<a <a
href={group.instagram} href={group.instagram}
target="_blank" target="_blank"
rel="noopener noreferrer external" rel="noopener noreferrer external"
aria-label={`Follow ${group.name} on Instagram`} aria-label={`Follow ${group.name} on Instagram`}
class="bg-ecsess-800 hover:bg-ecsess-500 text-ecsess-100 hover:text-ecsess-50 flex h-10 w-10 items-center justify-center rounded-lg shadow-md transition-all hover:shadow-lg active:scale-95" class="text-ecsess-300 hover:text-ecsess-100 border-ecsess-700 bg-ecsess-900/50 hover:bg-ecsess-800/80 inline-flex items-center gap-2 rounded-md border px-4 py-2 text-base"
> >
<Instagram <Instagram class="size-5" strokeWidth={2.5} aria-hidden="true" focusable="false" />
class="h-5 w-5" <span>Instagram</span>
strokeWidth={2.5} </a>
aria-hidden="true" {/if}
focusable="false" {#if group.website}
/> <a
href={group.website}
target="_blank"
rel="noopener noreferrer external"
aria-label={`Visit ${group.name} website`}
class="text-ecsess-300 hover:text-ecsess-100 border-ecsess-700 bg-ecsess-900/50 hover:bg-ecsess-800/80 inline-flex items-center gap-2 rounded-md border px-4 py-2 text-base"
>
<Globe class="size-5" strokeWidth={2.5} aria-hidden="true" focusable="false" />
<span>Website</span>
</a> </a>
{/if} {/if}
</div> </div>
</div> </div>
<!-- Description -->
<p class="text-ecsess-100 mb-6 flex-1 text-base leading-relaxed">
{group.description}
</p>
<!-- Features -->
<ul class="mb-6 space-y-2" role="list">
{#each group.features as feature (feature)}
<li class="flex items-center gap-2 pl-3">
<div class="bg-ecsess-500 h-1.5 w-1.5 rounded-full" aria-hidden="true"></div>
<span class="text-ecsess-100 text-base font-semibold">
{feature}
</span>
</li>
{/each}
</ul>
</div>
</article> </article>
{/each} {/each}
</div> </div>
<!-- Bottom CTA -->
<div class="mt-12 text-center">
<p class="text-ecsess-300 text-sm">
Want to get involved? Visit their websites and social media pages to learn about upcoming
events and how to join!
</p>
</div>
</div> </div>

View File

@@ -0,0 +1,58 @@
<script lang="ts">
import type { Sponsors } from '$lib/schemas';
import Link from 'components/Link.svelte';
import Button from 'components/Button.svelte';
let { sponsors } = $props<{ sponsors: Sponsors[] }>();
</script>
<div class="container mx-auto px-4">
<!-- Section Header -->
<div class="my-12 text-center">
<h2 id="sponsors-title" class="text-ecsess-100 mb-2 text-4xl font-bold md:text-5xl">
Our Sponsors
</h2>
<p class="text-ecsess-200/90 mx-auto max-w-2xl text-base leading-relaxed md:text-lg">
We're grateful to our sponsors for their continued support of ECSESS, our events, activities,
and our community.
</p>
<div
class="via-ecsess-150/40 mx-auto mt-6 h-px w-32 bg-linear-to-r from-transparent to-transparent"
aria-hidden="true"
></div>
<div class="mt-6">
<Link href="/sponsor">
<Button>Become a Sponsor</Button>
</Link>
</div>
</div>
<!-- Sponsors -->
{#if sponsors && sponsors.length > 0}
<div class="mx-auto max-w-6xl">
<div class="flex flex-wrap justify-center gap-4 sm:gap-6" aria-labelledby="sponsors-title">
{#each sponsors as sponsor}
<Link
href={sponsor.url}
external
class="group flex w-full max-w-[342px] justify-center rounded-xl focus-visible:outline-none"
>
<div
class="border-ecsess-150/15 bg-ecsess-950 group-hover:border-ecsess-150/30 group-hover:bg-ecsess-900 flex h-28 w-full items-center justify-center overflow-hidden rounded-xl border p-6 shadow-md transition-colors duration-150"
>
<img
src={sponsor.logo}
alt={sponsor.name}
class="max-h-16 w-full object-contain opacity-90 transition-opacity duration-150 group-hover:opacity-100"
/>
</div>
</Link>
{/each}
</div>
</div>
{:else}
<div class="text-ecsess-300 py-12 text-center">
<p>You can be our next sponsor!</p>
</div>
{/if}
</div>

View File

@@ -0,0 +1,10 @@
<script>
let { thumbnail } = $props();
</script>
<svelte:head>
{#if thumbnail}
<meta property="og:image" content={thumbnail} />
<meta property="twitter:image" content={thumbnail} />
{/if}
</svelte:head>

View File

@@ -4,9 +4,6 @@
description = 'Meet the student council, get access to academic and technical resources, registration for events, and much more!', description = 'Meet the student council, get access to academic and technical resources, registration for events, and much more!',
canonical = 'https://ecsess.mcgilleus.ca' canonical = 'https://ecsess.mcgilleus.ca'
} = $props(); } = $props();
let thumbnail =
'https://cdn.sanity.io/images/vmtsvpe2/production/5d68504038cc692805dc5e51af83adedfefde442-5304x3443.jpg?h=628&fm=webp';
</script> </script>
<svelte:head> <svelte:head>
@@ -22,12 +19,10 @@
<meta property="og:url" content={canonical} /> <meta property="og:url" content={canonical} />
<meta property="og:title" content={title} /> <meta property="og:title" content={title} />
<meta property="og:description" content={description} /> <meta property="og:description" content={description} />
<meta property="og:image" content={thumbnail} />
<!-- X (Twitter) --> <!-- X (Twitter) -->
<meta property="twitter:card" content="summary_large_image" /> <meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={canonical} /> <meta property="twitter:url" content={canonical} />
<meta property="twitter:title" content={title} /> <meta property="twitter:title" content={title} />
<meta property="twitter:description" content={description} /> <meta property="twitter:description" content={description} />
<meta property="twitter:image" content={thumbnail} />
</svelte:head> </svelte:head>

View File

@@ -9,14 +9,14 @@
</script> </script>
<div <div
class="bg-ecsess-100 text-ecsess-900 hover:bg-ecsess-200 border-ecsess-300 grid h-full place-content-center rounded-md border text-center shadow-md transition-all hover:shadow-lg" class="bg-ecsess-100 text-ecsess-900 hover:bg-ecsess-200 grid h-full place-content-center rounded-md text-center shadow-md transition-all hover:shadow-lg"
> >
<p class="text-base font-extrabold lg:text-lg"> <p class="text-base leading-tight font-semibold">
{officeHour.member.name.split(' ')[0]} {officeHour.member.name.split(' ')[0]}
</p> </p>
{#if !isShortBlock} {#if !isShortBlock}
<p class="text-ecsess-700 text-xs italic"> <p class="text-ecsess-700 mt-0.5 text-[11px] leading-tight opacity-90">
{shortenPosition(officeHour.member.position)} {shortenPosition(officeHour.member.position)}
</p> </p>
{/if} {/if}

View File

@@ -114,10 +114,14 @@
</script> </script>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<div class="mx-auto max-w-7xl min-w-[800px]"> <div class="border-ecsess-500 bg-ecsess-900 mx-auto max-w-7xl min-w-[800px] border-t pt-2">
<!-- Header row --> <!-- Header row -->
<div class="mb-2 grid gap-0" style:grid-template-columns="80px repeat(5, 1fr)"> <div class="mb-2 grid gap-0" style:grid-template-columns="80px repeat(5, 1fr)">
<div class="text-ecsess-50 px-2 text-center text-base font-semibold">Time</div> <div
class="text-ecsess-50 bg-ecsess-900 sticky left-0 z-20 px-2 text-center text-base font-semibold"
>
Time
</div>
{#each DAYS as day} {#each DAYS as day}
<div class="text-ecsess-50 px-2 text-center text-base font-semibold md:text-lg"> <div class="text-ecsess-50 px-2 text-center text-base font-semibold md:text-lg">
{day} {day}
@@ -135,7 +139,7 @@
<!-- Time column (only for first day) --> <!-- Time column (only for first day) -->
{#if dayIndex === 0} {#if dayIndex === 0}
<div class="border-ecsess-500 relative border-b-2"> <div class="border-ecsess-500 bg-ecsess-900 sticky left-0 z-20 border-b-2">
{#each timeSlots as timeSlot} {#each timeSlots as timeSlot}
{@const isHourMark = timeSlot % 60 === 0} {@const isHourMark = timeSlot % 60 === 0}
<div <div

View File

@@ -36,15 +36,6 @@ export type FAQ = {
answer: string; answer: string;
}; };
export type HomepageCMSResponse = {
description: InputValue;
councilPhoto: string;
faqs: {
question: string;
answer: string;
}[];
};
export type OfficeHour = { export type OfficeHour = {
day: string; day: string;
startTime: string; startTime: string;

View File

@@ -18,8 +18,8 @@
</Link> </Link>
<p class="text-ecsess-200 text-lg leading-relaxed md:text-xl"> <p class="text-ecsess-200 text-lg leading-relaxed md:text-xl">
The page you are looking for is not implemented because we are too lazy to do it. But if you The page you are looking for is not implemented because we are too lazy to do it. <br />
really want to see it, you can reboot to the homepage and try again. But if you really want to see it, you can reboot to the homepage and try again.
<br /> <br />
<br /> <br />
Or even better, you can join us and help us implement it. Or even better, you can join us and help us implement it.

View File

@@ -0,0 +1,14 @@
import { getFromCMS } from '$lib/utils.js';
const thumbnailQuery = `*[_type == "homepage"]{
"thumbnail": councilPhoto.asset->url+"?h=800&fm=webp",
}[0]`;
export const load = async () => {
try {
return { thumbnail: (await getFromCMS(thumbnailQuery)).thumbnail };
} catch (err) {
console.error('Failed to fetch homepage thumbnail from CMS:', err);
return { thumbnail: null };
}
};

View File

@@ -4,9 +4,9 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import Navbar from 'components/layout/NavBar.svelte'; import Navbar from 'components/layout/NavBar.svelte';
import Footer from 'components/layout/Footer.svelte'; import Footer from 'components/layout/Footer.svelte';
import PageThumbnail from 'components/layout/PageThumbnail.svelte';
let { children } = $props(); let { children, data } = $props();
// Lazy load analytics only in production for faster dev startup // Lazy load analytics only in production for faster dev startup
onMount(async () => { onMount(async () => {
if (!dev) { if (!dev) {
@@ -17,5 +17,6 @@
</script> </script>
<Navbar /> <Navbar />
<PageThumbnail thumbnail={data.thumbnail} />
{@render children()} {@render children()}
<Footer /> <Footer />

View File

@@ -1,13 +1,12 @@
import { getFromCMS } from '$lib/utils.js'; import { getFromCMS } from '$lib/utils.js';
import type { HomepageCMSResponse, OfficeHour, Sponsors } from '$lib/schemas'; import type { OfficeHour, Sponsors } from '$lib/schemas';
const homepageQuery = `*[_type == "homepage"]{ const homepageQuery = `{
"description": description[], "homepage": *[_type == "homepage"]{
"councilPhoto": councilPhoto.asset->url+"?h=1200&fm=webp", "councilPhoto": councilPhoto.asset->url+"?h=1200&fm=webp",
"faqs": faqs[]{ question, answer }, "faqs": faqs[]{ question, answer },
}[0]`; }[0],
"officeHours": *[_type=="officeHours"]{
const ohQuery = `*[_type=="officeHours"]{
day, day,
startTime, startTime,
endTime, endTime,
@@ -15,27 +14,26 @@ const ohQuery = `*[_type=="officeHours"]{
"name": member->name, "name": member->name,
"position": member->position "position": member->position
} }
}`; },
"sponsors": *[_type=="sponsors"]{
const sponsorQuery = `*[_type=="sponsors"]{
name, name,
url, url,
"logo": logo.asset->url+"?h=100&fm=webp" "logo": logo.asset->url+"?h=100&fm=webp"
}
}`; }`;
export const load = async ({ url }) => { export const load = async ({ url }) => {
/** /**
* @description Response data type based on the `homepageQuery` above. * @description Response data type based on the combined query above.
* Note that `description` is a rich/portable text type * Note that `description` is a rich/portable text type
*/ */
let homepageResp: HomepageCMSResponse = await getFromCMS(homepageQuery); let homePageResp = await getFromCMS(homepageQuery);
let officeHourResp: OfficeHour[] = await getFromCMS(ohQuery); let councilPhotoUrl: string = homePageResp.homepage.councilPhoto;
let sponsorsResp: Sponsors[] = await getFromCMS(sponsorQuery); let officeHourResp: OfficeHour[] = homePageResp.officeHours;
let sponsorsResp: Sponsors[] = homePageResp.sponsors;
return { return {
description: homepageResp.description, councilPhoto: councilPhotoUrl,
councilPhoto: homepageResp.councilPhoto,
faqs: homepageResp.faqs,
allOHs: officeHourResp, allOHs: officeHourResp,
sponsors: sponsorsResp, sponsors: sponsorsResp,
canonical: url.href canonical: url.href

View File

@@ -1,13 +1,11 @@
<script> <script>
import FaqAccordion from 'components/homepage/FAQAccordion.svelte';
import Section from 'components/layout/Section.svelte'; import Section from 'components/layout/Section.svelte';
import RichText from 'components/RichText.svelte';
import OhSchedule from 'components/officehour/OHSchedule.svelte'; import OhSchedule from 'components/officehour/OHSchedule.svelte';
import Link from 'components/Link.svelte';
import SeoMetaTags from 'components/layout/SeoMetaTags.svelte'; import SeoMetaTags from 'components/layout/SeoMetaTags.svelte';
import AffiliatedGroups from 'components/homepage/AffiliatedGroups.svelte'; import AffiliatedGroups from 'components/homepage/AffiliatedGroups.svelte';
import { fade } from 'svelte/transition'; import Sponsors from 'components/homepage/Sponsors.svelte';
import QuickLinks from 'components/QuickLinks.svelte'; import QuickLinks from 'components/QuickLinks.svelte';
import { fade } from 'svelte/transition';
/** loading things from the server side */ /** loading things from the server side */
let { data } = $props(); let { data } = $props();
@@ -17,86 +15,74 @@
<SeoMetaTags canonical={data.canonical} /> <SeoMetaTags canonical={data.canonical} />
<!-- ECSESS Introduction --> <!-- ECSESS Introduction -->
<Section from="from-ecsess-black" to="to-ecsess-900"> <Section from="from-ecsess-black" to="to-ecsess-900" via="via-ecsess-950">
<div <div
class="grid grid-cols-1 gap-2 place-self-center sm:gap-4 md:gap-6 lg:h-[70vh] lg:grid-cols-3 lg:grid-rows-3 lg:items-center lg:gap-6" class="mx-auto grid w-full max-w-[84dvw] grid-cols-1 place-items-center gap-16 py-6 lg:min-h-[75vh] lg:grid-cols-[1fr_2fr]"
>
<!-- Title -->
<div
class="order-1 mb-2 flex items-center justify-center lg:col-start-1 lg:row-start-1 lg:mb-6 lg:place-self-center"
>
<div class="flex flex-col text-center">
<p>
{#each 'We are ECSESS!' as char, i}
<span class="page-title" in:fade|global={{ delay: 200 + i * 100, duration: 800 }}
>{char}</span
> >
<!-- Left: Description and Quick Links -->
<div class="ml-4 flex flex-col items-center gap-2 text-center lg:items-start lg:text-left">
<h1 class="mb-2">
{#each 'We are ECSESS!'.split('') as char, i}
<span class="page-title" in:fade|global={{ delay: 150 + i * 60, duration: 800 }}>
{char}
</span>
{/each} {/each}
</p> </h1>
</div> <p
</div> class="text-ecsess-200/90 max-w-xl text-base leading-relaxed md:text-lg lg:max-w-lg lg:leading-8"
<!-- Description -->
<div
class="order-2 mb-2 flex items-center justify-center p-4 lg:col-start-1 lg:row-start-2 lg:mb-6 lg:place-self-center"
> >
<div class="max-w-xl text-center lg:text-center"> <span class="text-ecsess-50 font-bold"
<RichText value={data.description} /> >Electrical, Computer & Software Engineering Students' Society at McGill (ECSESS)</span
>
is the student council which helps McGill ECSE students in their
<span class="text-ecsess-50 font-bold">academic</span>,
<span class="text-ecsess-50 font-bold">technical</span>,
<span class="text-ecsess-50 font-bold">social</span> and
<span class="text-ecsess-50 font-bold">professional</span> progression.
</p>
<div class="mt-8 w-full max-w-xl lg:max-w-none">
<QuickLinks />
</div> </div>
</div> </div>
<!-- Image --> <!-- Right: Council Photo -->
<div class="order-3 m-0 sm:m-2 lg:col-span-2 lg:col-start-2 lg:row-span-3 lg:row-start-1"> <div class="relative flex w-full items-center justify-center lg:max-w-none">
<div class="flex h-auto w-full items-center justify-center sm:h-full"> <div
class="ring-ecsess-400/20 ring-offset-ecsess-900/50 relative flex items-center justify-center overflow-hidden rounded-2xl shadow-2xl ring-1 ring-offset-2"
>
<div
class="from-ecsess-500/10 absolute inset-0 rounded-2xl bg-linear-to-br to-transparent"
aria-hidden="true"
></div>
<img <img
src={data.councilPhoto} src={data.councilPhoto}
alt="ECSESS Council" alt="ECSESS Council"
class="ring-ecsess-500 max-h-[35vh] max-w-full rounded-md object-contain shadow-md ring-4 transition-all sm:max-h-[45vh] md:max-h-[60vh] lg:max-h-full" class="relative h-full w-full object-contain object-center"
/> />
</div> </div>
</div> </div>
<!-- Quick Links -->
<div class="order-4 p-3 lg:col-start-1 lg:row-start-3 lg:flex lg:place-self-center lg:p-4">
<QuickLinks />
</div>
</div> </div>
</Section> </Section>
<!-- Office Hours Calendar --> <!-- Office Hours Calendar -->
<Section from="from-ecsess-900" to="to-ecsess-800"> <Section from="from-ecsess-900" to="to-ecsess-700" via="via-ecsess-650">
<div class="w-full"> <div class="w-full">
<h1 id="office-hours">Office Hours</h1> <h2 class="text-2xl font-bold" id="office-hours">Lounge Office Hours</h2>
<p class="text-ecsess-200 mb-8">
Come visit us in our student lounge at ENGTR 1060 to grab a coffee (free), play Mario Kart, or
just chat about anything!
</p>
<OhSchedule allOhs={data.allOHs} /> <OhSchedule allOhs={data.allOHs} />
</div> </div>
</Section> </Section>
<!-- Sponsors -->
<Section from="from-ecsess-700" to="to-ecsess-800" via="via-ecsess-750">
<Sponsors sponsors={data.sponsors} />
</Section>
<!-- Affiliated Clubs --> <!-- Affiliated Clubs -->
<Section from="from-ecsess-800" to="to-ecsess-950"> <Section from="from-ecsess-800" to="to-ecsess-black" via="via-ecsess-850">
<AffiliatedGroups /> <AffiliatedGroups />
</Section> </Section>
<!-- FAQs and Sponsors -->
<Section from="from-ecsess-950" to="to-ecsess-black">
<div class="grid w-full max-w-[80vw] grid-cols-1 gap-12 lg:grid-cols-2 lg:gap-24">
<div>
<h1>FAQs</h1>
<hr class="hr w-full border-dashed py-4" />
<FaqAccordion entries={data.faqs} />
</div>
<div id="sponsors" class="mb-24">
<h1>Sponsors</h1>
<hr class="hr w-full border-dashed py-4" />
<div class="flex gap-12">
{#each data.sponsors as sponsor}
<div class="max-h-20">
<Link href={sponsor.url}>
<img src={sponsor.logo} alt="{sponsor.name} Logo" class="max-h-24" />
</Link>
</div>
{/each}
</div>
</div>
</div>
</Section>