Merge pull request #38 from mcgill-ecsess/SEO

Search Engine Optimization


Co-authored-by: Antoine Phan <antoine.phan@mail.mcgill.ca>
This commit is contained in:
ECSESS VP Tech Dev
2025-09-06 10:13:56 -04:00
committed by GitHub
12 changed files with 101 additions and 24 deletions

View File

@@ -0,0 +1,32 @@
<script>
let {
title = "Electrical, Computer & Software Engineering Students' Society at McGill - ECSESS",
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>
<!-- Meta Tags Generated with https://metatags.io -->
<!-- Primary Meta Tags -->
<title>{title}</title>
<meta name="title" content={title} />
<meta name="description" content={description} />
<link rel="canonical" href={canonical} />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<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>

View File

@@ -23,7 +23,7 @@ const sponsorQuery = `*[_type=="sponsors"]{
"logo": logo.asset->url+"?h=100&fm=webp" "logo": logo.asset->url+"?h=100&fm=webp"
}`; }`;
export const load = async () => { export const load = async ({ url }) => {
/** /**
* @description Response data type based on the `homepageQuery` above. * @description Response data type based on the `homepageQuery` above.
* Note that `description` is a rich/portable text type * Note that `description` is a rich/portable text type
@@ -37,6 +37,7 @@ export const load = async () => {
councilPhoto: homepageResp.councilPhoto, councilPhoto: homepageResp.councilPhoto,
faqs: homepageResp.faqs, faqs: homepageResp.faqs,
allOHs: officeHourResp, allOHs: officeHourResp,
sponsors: sponsorsResp sponsors: sponsorsResp,
canonical: url.href
}; };
}; };

View File

@@ -4,6 +4,7 @@
import RichText from 'components/RichText.svelte'; import RichText from 'components/RichText.svelte';
import OhSchedule from 'components/OHSchedule.svelte'; import OhSchedule from 'components/OHSchedule.svelte';
import Link from 'components/Link.svelte'; import Link from 'components/Link.svelte';
import SeoMetaTags from 'components/SeoMetaTags.svelte';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
/** loading things from the server side */ /** loading things from the server side */
@@ -11,10 +12,11 @@
// Temporary progress bar. Update the value below! // Temporary progress bar. Update the value below!
import { Progress } from '@skeletonlabs/skeleton-svelte'; import { Progress } from '@skeletonlabs/skeleton-svelte';
let progress = 63.33; let progress = 80;
</script> </script>
<title> McGill ECSESS </title> <!-- SEO Meta header tags. Root page can use default values -->
<SeoMetaTags canonical={data.canonical} />
<!-- ECSESS Introduction --> <!-- ECSESS Introduction -->
<Section> <Section>
@@ -23,7 +25,9 @@
<div class="flex h-1/2 flex-col place-content-center text-center"> <div class="flex h-1/2 flex-col place-content-center text-center">
<p> <p>
{#each 'We are ECSESS!' as char, i} {#each 'We are ECSESS!' as char, i}
<span class="page-title" in:fade|global={{ delay: 200 + i * 100, duration: 800 }}>{char}</span> <span class="page-title" in:fade|global={{ delay: 200 + i * 100, duration: 800 }}
>{char}</span
>
{/each} {/each}
</p> </p>
<div class="p-4"> <div class="p-4">

View File

@@ -17,10 +17,12 @@ const councilGoofyPicQuery = `*[_type == "homepage"]{
"url": councilGoofyPic.asset->url+"?h=1200&fm=webp" "url": councilGoofyPic.asset->url+"?h=1200&fm=webp"
}[0]`; }[0]`;
export const load = async () => { export const load = async ({ url }) => {
let councilMembers: CouncilMember[] = await getFromCMS(councilQuery); let councilMembers: CouncilMember[] = await getFromCMS(councilQuery);
return { return {
members: councilMembers, members: councilMembers,
councilGoofyPic: await getFromCMS(councilGoofyPicQuery) councilGoofyPic: await getFromCMS(councilGoofyPicQuery),
canonical: url.href
}; };
}; };

View File

@@ -5,6 +5,7 @@
import type { CouncilMember } from '$lib/schemas'; import type { CouncilMember } from '$lib/schemas';
import { fly } from 'svelte/transition'; import { fly } from 'svelte/transition';
import Button from 'components/Button.svelte'; import Button from 'components/Button.svelte';
import SeoMetaTags from 'components/SeoMetaTags.svelte';
let { data } = $props(); let { data } = $props();
@@ -38,7 +39,12 @@
} }
</script> </script>
<title> ECSESS council </title> <SeoMetaTags
title="Meet the ECSESS student council members!"
description="Greetings from ECSESS student council!"
canonical={data.canonical}
/>
<Section> <Section>
<div class="flex flex-col place-items-center"> <div class="flex flex-col place-items-center">
<p class="page-title">Meet the council!</p> <p class="page-title">Meet the council!</p>

View File

@@ -9,17 +9,19 @@ const eventQuery = `*[_type == "events"]{
description, description,
reglink, reglink,
paylink, paylink,
"thumbnail": thumbnail.asset->url+"?h=700&fm=webp", "thumbnail": thumbnail.asset->url+"?h=800&fm=webp",
"lastUpdated": _updatedAt, "lastUpdated": _updatedAt,
}`; }`;
export const load = async () => { export const load = async ({ url }) => {
let listOfEvents: EventPost[] = await getFromCMS(eventQuery); let listOfEvents: EventPost[] = await getFromCMS(eventQuery);
//TODO: Why is this mad?
let sortedEvents = listOfEvents.sort( let sortedEvents = listOfEvents.sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime() (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
); );
return { return {
events: sortedEvents events: sortedEvents,
canonical: url.href
}; };
}; };

View File

@@ -1,18 +1,30 @@
<script lang="ts"> <script lang="ts">
import Section from 'components/Section.svelte'; import Section from 'components/Section.svelte';
import SeoMetaTags from 'components/SeoMetaTags.svelte';
let { data } = $props();
import EventTabControl from 'components/EventTabControl.svelte'; import EventTabControl from 'components/EventTabControl.svelte';
import { Tabs } from '@skeletonlabs/skeleton-svelte'; import { Tabs } from '@skeletonlabs/skeleton-svelte';
import EventTabPanel from 'components/EventTabPanel.svelte'; import EventTabPanel from 'components/EventTabPanel.svelte';
import type { EventPost } from '$lib/schemas'; import type { EventPost } from '$lib/schemas';
let { data } = $props();
let events: EventPost[] = data.events ?? []; let events: EventPost[] = data.events ?? [];
let group = $state('allEvents'); let group = $state('allEvents');
</script> </script>
<SeoMetaTags
title="Events by ECSESS"
description="Checkout our events! ECSESS organizes academic events, professional & social networkings, technical workshops, and more!"
canonical={data.canonical}
/>
<Section> <Section>
<p class="page-title">Events</p> <p class="page-title">Events</p>
<Tabs value={group} onValueChange={(e) => (group = e.value)} listClasses="flex-wrap place-content-center"> <Tabs
value={group}
onValueChange={(e) => (group = e.value)}
listClasses="flex-wrap place-content-center"
>
{#snippet list()} {#snippet list()}
<EventTabControl value="allEvents">All Events</EventTabControl> <EventTabControl value="allEvents">All Events</EventTabControl>
<EventTabControl value="academic">Academic</EventTabControl> <EventTabControl value="academic">Academic</EventTabControl>

View File

@@ -0,0 +1,5 @@
export const load = async ({ url }) => {
return {
canonical: url.href
};
};

View File

@@ -1,19 +1,24 @@
<script> <script>
import ResourceCard from 'components/ResourceCard.svelte'; import ResourceCard from 'components/ResourceCard.svelte';
import Section from 'components/Section.svelte'; import Section from 'components/Section.svelte';
import SeoMetaTags from 'components/SeoMetaTags.svelte';
let isElectionTime = $state(true); let isElectionTime = $state(true);
let { data } = $props();
</script> </script>
<title> Join ECSESS !!! </title> <SeoMetaTags
title="Join the ECSESS student council!!!"
description="Learn how you can join the ECSESS council and make an impact on the ECSE student community at McGill University!"
canonical={data.canonical}
/>
<Section> <Section>
<p class="page-title">Want to join ECSESS Council?</p> <p class="page-title">Want to join ECSESS Council?</p>
<p>Come back around March for application period!</p>
{#if isElectionTime} {#if isElectionTime}
<ResourceCard title="Involvement Booklet"> <ResourceCard title="Involvement Booklet">
A guide to involvement with ECSESS and its subcommittees (The Factory, IEEE McGill, CodeJam). A guide to involvement with ECSESS and its subcommittees (The Factory, IEEE McGill, CodeJam).
</ResourceCard> </ResourceCard>
{:else} {:else}{/if}
<p>Come back around March for application period!</p>
{/if}
</Section> </Section>

View File

@@ -3,6 +3,8 @@
let { data } = $props(); let { data } = $props();
</script> </script>
<title> Hmm... you're not supposed to be here :/ </title>
<Section> <Section>
<p class="page-title">Can't redirect you to <code>"r/{data.shortname}"</code>!</p> <p class="page-title">Can't redirect you to <code>"r/{data.shortname}"</code>!</p>
<hr class="border-2 w-1/2"> <hr class="border-2 w-1/2">

View File

@@ -8,9 +8,10 @@ const query = `*[_type == "resources"]{
description, description,
}`; }`;
export const load = async () => { export const load = async ({ url }) => {
const resources: Resource[] = await getFromCMS(query); const resources: Resource[] = await getFromCMS(query);
return { return {
resources: resources resources: resources,
canonical: url.href
}; };
}; };

View File

@@ -1,10 +1,15 @@
<script> <script>
import ResourceCard from 'components/ResourceCard.svelte'; import ResourceCard from 'components/ResourceCard.svelte';
import Section from 'components/Section.svelte'; import Section from 'components/Section.svelte';
import SeoMetaTags from 'components/SeoMetaTags.svelte';
let { data } = $props(); let { data } = $props();
</script> </script>
<title> Resources </title> <SeoMetaTags
title="ECSESS Resources - Academic, Technical, Involvement, etc."
description="ECSESS resource hub for everything relating to academic, technical, involvement, sustainability, equity, campus life, and more!"
canonical={data.canonical}
/>
<Section> <Section>
<p class="page-title">Resources</p> <p class="page-title">Resources</p>