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:
16
src/app.css
16
src/app.css
@@ -6,21 +6,29 @@
|
||||
/* Light shades - for backgrounds and cards */
|
||||
--color-ecsess-50: #e8ffd9;
|
||||
--color-ecsess-100: #cce7ba;
|
||||
--color-ecsess-150: #bae9a5;
|
||||
--color-ecsess-200: #a9d0a0;
|
||||
|
||||
/* Mid-light shades - for borders and hover states */
|
||||
--color-ecsess-250: #9cc295;
|
||||
--color-ecsess-300: #8fb98a;
|
||||
--color-ecsess-350: #7daa7a;
|
||||
--color-ecsess-400: #6a9a6a;
|
||||
|
||||
/* Mid shades - for accents and interactive elements */
|
||||
--color-ecsess-450: #62925a;
|
||||
--color-ecsess-500: #5a8b5a;
|
||||
--color-ecsess-550: #4c7a4f;
|
||||
--color-ecsess-600: #3f6a3f;
|
||||
--color-ecsess-650: #306032;
|
||||
|
||||
/* Mid-dark shades - for text on light backgrounds */
|
||||
--color-ecsess-700: #2d5a2d;
|
||||
--color-ecsess-700: #2f4d29;
|
||||
--color-ecsess-750: #1c4a1e;
|
||||
--color-ecsess-800: #0a3d2a;
|
||||
|
||||
/* Dark shades - for text and backgrounds */
|
||||
--color-ecsess-850: #083525;
|
||||
--color-ecsess-900: #062c20;
|
||||
--color-ecsess-950: #031c15;
|
||||
|
||||
@@ -28,14 +36,14 @@
|
||||
--color-ecsess-black: #1f1f1f;
|
||||
--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 {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotateY(-3deg);
|
||||
transform: rotateY(-2.4deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotateY(3deg);
|
||||
transform: rotateY(2.4deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
let { href, external = false, children } = $props();
|
||||
let { href, external = false, children, class: className = '' } = $props();
|
||||
</script>
|
||||
|
||||
<a
|
||||
{href}
|
||||
target={external ? '_blank' : undefined}
|
||||
rel={external ? 'noopener noreferrer' : undefined}
|
||||
class={className}
|
||||
>
|
||||
{@render children()}
|
||||
</a>
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
let { value } = $props();
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col justify-center-safe">
|
||||
<div class="typography flex flex-col justify-center-safe">
|
||||
<PortableText {value} />
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<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
|
||||
type IconComponent = typeof Wrench;
|
||||
@@ -17,16 +17,11 @@
|
||||
{
|
||||
name: 'Code.Jam()',
|
||||
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/',
|
||||
instagram: 'https://www.instagram.com/mcgillcodejam/',
|
||||
icon: CodeXml,
|
||||
features: [
|
||||
'Biggest Hackathon in Engineering',
|
||||
'Great prizes',
|
||||
'Cool swags and merch',
|
||||
'Networking opportunities'
|
||||
]
|
||||
features: ['Biggest Hackathon in Engineering', 'Great prizes', 'Networking opportunities']
|
||||
},
|
||||
{
|
||||
name: 'The Factory',
|
||||
@@ -35,7 +30,16 @@
|
||||
website: 'https://factory.mcgilleus.ca/',
|
||||
instagram: 'https://www.instagram.com/thefactory_mcgill/',
|
||||
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',
|
||||
@@ -43,114 +47,95 @@
|
||||
'One of the largest IEEE student branches in Eastern Canada, offering professional development, networking, and industry connections.',
|
||||
website: 'https://ieee.mcgilleus.ca/',
|
||||
instagram: 'https://www.instagram.com/ieeemcgill/',
|
||||
icon: Zap,
|
||||
features: [
|
||||
'Technical Talks',
|
||||
'Arduino Workshops',
|
||||
'IEEEXtreme Competition',
|
||||
'Networking Events'
|
||||
]
|
||||
icon: Cpu,
|
||||
features: ['Technical Talks', 'Arduino Workshops', 'Networking Events']
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto px-4">
|
||||
<!-- Section Header -->
|
||||
<div class="mb-12 text-center">
|
||||
<h2 id="affiliated-clubs-title" class="text-ecsess-100 mb-4 text-4xl font-bold md:text-5xl">
|
||||
<div class="my-12 text-center">
|
||||
<h2 id="affiliated-clubs-title" class="text-ecsess-100 mb-2 text-4xl font-bold md:text-5xl">
|
||||
Subcommittees & Affiliated Groups
|
||||
</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
|
||||
engineering community through our subcommittees and affiliated groups.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Clubs Grid -->
|
||||
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||
<!-- Clubs Grid: 2x2 on large screens -->
|
||||
<div class="grid grid-cols-1 gap-8 md:grid-cols-2">
|
||||
{#each groups as group, i (group.name)}
|
||||
{@const Icon = group.icon}
|
||||
<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`}
|
||||
>
|
||||
<!-- Decorative gradient bar -->
|
||||
<div class="from-ecsess-400 via-ecsess-500 to-ecsess-600 h-2 bg-gradient-to-r"></div>
|
||||
|
||||
<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
|
||||
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"
|
||||
>
|
||||
<Icon
|
||||
class="text-ecsess-300 h-7 w-7 transition-colors group-hover:text-white"
|
||||
strokeWidth={2.5}
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
/>
|
||||
</div>
|
||||
<h3 id={`group-${i}-title`} class="text-ecsess-50 text-2xl font-bold">
|
||||
{group.name}
|
||||
</h3>
|
||||
<div class="flex flex-1 flex-col p-7 md:p-8">
|
||||
<!-- Header: icon + name -->
|
||||
<header class="mb-5 flex items-center justify-start gap-4">
|
||||
<div
|
||||
class="bg-ecsess-800 flex h-14 w-14 shrink-0 items-center justify-center rounded-xl"
|
||||
>
|
||||
<Icon
|
||||
class="text-ecsess-300 size-7"
|
||||
strokeWidth={2.5}
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
/>
|
||||
</div>
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex gap-2">
|
||||
<h3 id={`group-${i}-title`} class="text-ecsess-50 text-2xl font-bold">
|
||||
{group.name}
|
||||
</h3>
|
||||
</header>
|
||||
|
||||
<!-- Description -->
|
||||
<p class="text-ecsess-200 mb-5 text-base leading-relaxed md:text-lg">
|
||||
{group.description}
|
||||
</p>
|
||||
|
||||
<!-- Features -->
|
||||
<ul class="mb-5 list-none space-y-2 ps-0 text-base md:text-lg" role="list">
|
||||
{#each group.features as feature (feature)}
|
||||
<li class="flex items-center gap-2">
|
||||
<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}
|
||||
<a
|
||||
href={group.instagram}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer external"
|
||||
aria-label={`Follow ${group.name} on Instagram`}
|
||||
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 class="size-5" strokeWidth={2.5} aria-hidden="true" focusable="false" />
|
||||
<span>Instagram</span>
|
||||
</a>
|
||||
{/if}
|
||||
{#if group.website}
|
||||
<a
|
||||
href={group.website}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer external"
|
||||
aria-label={`Visit ${group.name} website`}
|
||||
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"
|
||||
>
|
||||
<Globe class="h-5 w-5" strokeWidth={2.5} aria-hidden="true" focusable="false" />
|
||||
<Globe class="size-5" strokeWidth={2.5} aria-hidden="true" focusable="false" />
|
||||
<span>Website</span>
|
||||
</a>
|
||||
{#if group.instagram}
|
||||
<a
|
||||
href={group.instagram}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer external"
|
||||
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"
|
||||
>
|
||||
<Instagram
|
||||
class="h-5 w-5"
|
||||
strokeWidth={2.5}
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
/>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</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>
|
||||
{/each}
|
||||
</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>
|
||||
|
||||
58
src/components/homepage/Sponsors.svelte
Normal file
58
src/components/homepage/Sponsors.svelte
Normal 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>
|
||||
10
src/components/layout/PageThumbnail.svelte
Normal file
10
src/components/layout/PageThumbnail.svelte
Normal 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>
|
||||
@@ -4,9 +4,6 @@
|
||||
description = 'Meet the student council, get access to academic and technical resources, registration for events, and much more!',
|
||||
canonical = 'https://ecsess.mcgilleus.ca'
|
||||
} = $props();
|
||||
|
||||
let thumbnail =
|
||||
'https://cdn.sanity.io/images/vmtsvpe2/production/5d68504038cc692805dc5e51af83adedfefde442-5304x3443.jpg?h=628&fm=webp';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -22,12 +19,10 @@
|
||||
<meta property="og:url" content={canonical} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={thumbnail} />
|
||||
|
||||
<!-- X (Twitter) -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content={canonical} />
|
||||
<meta property="twitter:title" content={title} />
|
||||
<meta property="twitter:description" content={description} />
|
||||
<meta property="twitter:image" content={thumbnail} />
|
||||
</svelte:head>
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
</script>
|
||||
|
||||
<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]}
|
||||
</p>
|
||||
|
||||
{#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)}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
@@ -114,10 +114,14 @@
|
||||
</script>
|
||||
|
||||
<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 -->
|
||||
<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}
|
||||
<div class="text-ecsess-50 px-2 text-center text-base font-semibold md:text-lg">
|
||||
{day}
|
||||
@@ -135,7 +139,7 @@
|
||||
|
||||
<!-- Time column (only for first day) -->
|
||||
{#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}
|
||||
{@const isHourMark = timeSlot % 60 === 0}
|
||||
<div
|
||||
|
||||
@@ -36,15 +36,6 @@ export type FAQ = {
|
||||
answer: string;
|
||||
};
|
||||
|
||||
export type HomepageCMSResponse = {
|
||||
description: InputValue;
|
||||
councilPhoto: string;
|
||||
faqs: {
|
||||
question: string;
|
||||
answer: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type OfficeHour = {
|
||||
day: string;
|
||||
startTime: string;
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
</Link>
|
||||
|
||||
<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
|
||||
really want to see it, you can reboot to the homepage and try again.
|
||||
The page you are looking for is not implemented because we are too lazy to do it. <br />
|
||||
But if you really want to see it, you can reboot to the homepage and try again.
|
||||
<br />
|
||||
<br />
|
||||
Or even better, you can join us and help us implement it.
|
||||
|
||||
14
src/routes/+layout.server.ts
Normal file
14
src/routes/+layout.server.ts
Normal 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 };
|
||||
}
|
||||
};
|
||||
@@ -4,9 +4,9 @@
|
||||
import { onMount } from 'svelte';
|
||||
import Navbar from 'components/layout/NavBar.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
|
||||
onMount(async () => {
|
||||
if (!dev) {
|
||||
@@ -17,5 +17,6 @@
|
||||
</script>
|
||||
|
||||
<Navbar />
|
||||
<PageThumbnail thumbnail={data.thumbnail} />
|
||||
{@render children()}
|
||||
<Footer />
|
||||
|
||||
@@ -1,41 +1,39 @@
|
||||
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"]{
|
||||
"description": description[],
|
||||
"councilPhoto": councilPhoto.asset->url+"?h=1200&fm=webp",
|
||||
"faqs": faqs[]{ question, answer },
|
||||
}[0]`;
|
||||
|
||||
const ohQuery = `*[_type=="officeHours"]{
|
||||
day,
|
||||
startTime,
|
||||
endTime,
|
||||
"member": {
|
||||
"name": member->name,
|
||||
"position": member->position
|
||||
}
|
||||
}`;
|
||||
|
||||
const sponsorQuery = `*[_type=="sponsors"]{
|
||||
const homepageQuery = `{
|
||||
"homepage": *[_type == "homepage"]{
|
||||
"councilPhoto": councilPhoto.asset->url+"?h=1200&fm=webp",
|
||||
"faqs": faqs[]{ question, answer },
|
||||
}[0],
|
||||
"officeHours": *[_type=="officeHours"]{
|
||||
day,
|
||||
startTime,
|
||||
endTime,
|
||||
"member": {
|
||||
"name": member->name,
|
||||
"position": member->position
|
||||
}
|
||||
},
|
||||
"sponsors": *[_type=="sponsors"]{
|
||||
name,
|
||||
url,
|
||||
"logo": logo.asset->url+"?h=100&fm=webp"
|
||||
}
|
||||
}`;
|
||||
|
||||
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
|
||||
*/
|
||||
let homepageResp: HomepageCMSResponse = await getFromCMS(homepageQuery);
|
||||
let officeHourResp: OfficeHour[] = await getFromCMS(ohQuery);
|
||||
let sponsorsResp: Sponsors[] = await getFromCMS(sponsorQuery);
|
||||
let homePageResp = await getFromCMS(homepageQuery);
|
||||
let councilPhotoUrl: string = homePageResp.homepage.councilPhoto;
|
||||
let officeHourResp: OfficeHour[] = homePageResp.officeHours;
|
||||
let sponsorsResp: Sponsors[] = homePageResp.sponsors;
|
||||
|
||||
return {
|
||||
description: homepageResp.description,
|
||||
councilPhoto: homepageResp.councilPhoto,
|
||||
faqs: homepageResp.faqs,
|
||||
councilPhoto: councilPhotoUrl,
|
||||
allOHs: officeHourResp,
|
||||
sponsors: sponsorsResp,
|
||||
canonical: url.href
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<script>
|
||||
import FaqAccordion from 'components/homepage/FAQAccordion.svelte';
|
||||
import Section from 'components/layout/Section.svelte';
|
||||
import RichText from 'components/RichText.svelte';
|
||||
import OhSchedule from 'components/officehour/OHSchedule.svelte';
|
||||
import Link from 'components/Link.svelte';
|
||||
import SeoMetaTags from 'components/layout/SeoMetaTags.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 { fade } from 'svelte/transition';
|
||||
|
||||
/** loading things from the server side */
|
||||
let { data } = $props();
|
||||
@@ -17,86 +15,74 @@
|
||||
<SeoMetaTags canonical={data.canonical} />
|
||||
|
||||
<!-- 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
|
||||
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
|
||||
>
|
||||
{/each}
|
||||
</p>
|
||||
<!-- 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}
|
||||
</h1>
|
||||
<p
|
||||
class="text-ecsess-200/90 max-w-xl text-base leading-relaxed md:text-lg lg:max-w-lg lg:leading-8"
|
||||
>
|
||||
<span class="text-ecsess-50 font-bold"
|
||||
>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>
|
||||
|
||||
<!-- 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">
|
||||
<RichText value={data.description} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image -->
|
||||
<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="flex h-auto w-full items-center justify-center sm:h-full">
|
||||
<!-- Right: Council Photo -->
|
||||
<div class="relative flex w-full items-center justify-center lg:max-w-none">
|
||||
<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
|
||||
src={data.councilPhoto}
|
||||
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>
|
||||
|
||||
<!-- 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>
|
||||
</Section>
|
||||
|
||||
<!-- 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">
|
||||
<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} />
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<!-- Sponsors -->
|
||||
<Section from="from-ecsess-700" to="to-ecsess-800" via="via-ecsess-750">
|
||||
<Sponsors sponsors={data.sponsors} />
|
||||
</Section>
|
||||
|
||||
<!-- 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 />
|
||||
</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>
|
||||
|
||||
Reference in New Issue
Block a user