Redesign and update 'Meet the Team' data #452

Merged
j285he merged 17 commits from j285he-meet-the-team-s22 into main 2022-06-17 19:53:16 -04:00
59 changed files with 1822 additions and 2099 deletions
Showing only changes of commit 0b7dc42bd3 - Show all commits

View File

@ -3,7 +3,7 @@
}
.page > h1 {
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
padding-bottom: 1rem;
}

View File

@ -6,7 +6,6 @@
.bubble {
--border-radius: calc(5000rem / 16);
display: flex;
flex-direction: row;
position: absolute;

View File

@ -2,8 +2,8 @@
.link {
font-family: "Poppins", "sans-serif";
border-radius: calc(20rem / 16);
background-color: var(--primary-accent);
color: var(--primary-background);
background-color: var(--button-background);
color: var(--text-light);
border: none;
outline: none;
transition-duration: 0.3s;

View File

@ -4,7 +4,7 @@
max-width: calc(540rem / 16);
padding: calc(24rem / 16);
border-radius: calc(20rem / 16);
background-color: var(--primary-background);
background-color: var(--card-background);
}
.poster {
@ -38,7 +38,7 @@
.setting {
margin: 0;
color: var(--primary-accent);
color: var(--link);
font-size: calc(14rem / 16);
font-weight: 600;
}

View File

@ -1,6 +1,6 @@
.footer {
box-sizing: border-box;
background: var(--primary-heading);
background: var(--footer-background);
padding: 1rem 0;
width: 100%;
}
@ -17,7 +17,7 @@
}
.text {
color: var(--primary-background);
color: var(--text-light);
font-style: normal;
text-align: center;
}

View File

@ -1,11 +1,15 @@
import Link from "next/link";
import React from "react";
import { Button } from "./Button";
import { SocialLinks } from "./SocialLinks";
import { useThemeContext } from "./Theme";
import styles from "./Footer.module.css";
export function Footer() {
const themeContext = useThemeContext();
return (
<footer className={styles.footer}>
<div className={styles.container}>
@ -15,6 +19,16 @@ export function Footer() {
<a className={styles.email}>exec@csclub.uwaterloo.ca</a>
</Link>
</div>
<Button
size="small"
onClick={() =>
themeContext?.theme.name === "dark"
? themeContext?.setTheme("light")
: themeContext?.setTheme("dark")
}
>
Toggle Theme
</Button>
<SocialLinks color="white" size="small" />
</div>
</footer>

View File

@ -1,5 +1,5 @@
.link {
color: var(--primary-accent);
color: var(--link);
transition-duration: 0.3s;
text-decoration: none;
white-space: normal;
@ -7,5 +7,5 @@
}
.link:hover {
color: var(--secondary-accent);
color: var(--link-hover);
}

View File

@ -2,10 +2,11 @@
box-sizing: border-box;
position: relative;
padding: calc(20rem / 16);
color: var(--text);
}
.card:nth-child(odd) {
background-color: var(--secondary-accent-light);
.darkBg {
background-color: var(--dark-card-background);
}
.name {
@ -61,13 +62,15 @@
fill: var(--primary-accent);
}
.card h1,
.card h2,
.card h3,
.card h4 {
font-size: calc(16rem / 16);
font-size: calc(18rem / 16);
margin-top: calc(24rem / 16);
margin-bottom: calc(8rem / 16);
color: var(--mini-event-card-text);
}
@media only screen and (max-width: calc(768rem / 16)) {

View File

@ -13,6 +13,7 @@ interface MiniEventCardProps {
location: string;
startDate: Date;
endDate?: Date;
background: "dark-bg" | "normal-bg";
year: string;
term: string;
slug: string;
@ -26,12 +27,15 @@ export const MiniEventCard: React.FC<MiniEventCardProps> = ({
startDate,
endDate,
online,
background,
year,
term,
slug,
}) => {
const cardBackground =
background === "dark-bg" ? `${styles.darkBg} ${styles.card}` : styles.card;
return (
<details className={styles.card}>
<details className={cardBackground}>
<summary>
<div onClick={(event) => event.preventDefault()}>
<h2 className={styles.name}>

View File

@ -63,7 +63,7 @@
}
.navMenu a {
color: var(--primary-heading);
color: var(--primary-text);
text-decoration: none;
}
@ -219,6 +219,14 @@
cursor: pointer;
}
.icon line {
stroke: var(--icon);
}
.icon path {
fill: var(--icon);
}
.navMobileBackground {
position: fixed;
visibility: hidden;

View File

@ -120,7 +120,7 @@ export function Navbar() {
className={styles.hamburger}
onClick={() => dispatch({ type: "open", route: router.pathname })}
>
<Image src="/images/hamburger.svg" alt="Menu" />
<HamburgerSvg />
</button>
<div
className={
@ -263,7 +263,7 @@ function NavItem(props: NavItemProps) {
}
onClick={() => props.onToggle(props.route)}
>
<Image src="/images/dropdown-icon.svg" alt="Dropdown Icon" />
<DropdownSvg />
</button>
<ul
className={
@ -348,3 +348,64 @@ function getMainRoute(route: string) {
}
return "/" + route.split("/")[1];
}
function HamburgerSvg() {
return (
<svg
width="30"
height="23"
viewBox="0 0 30 23"
className={styles.icon}
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="28"
y1="2"
x2="2"
y2="2"
stroke="#2A2A62"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
<line
x1="28"
y1="11.375"
x2="2"
y2="11.375"
stroke="#2A2A62"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
<line
x1="28"
y1="20.75"
x2="2"
y2="20.75"
stroke="#2A2A62"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
function DropdownSvg() {
return (
<svg
width="14"
height="9"
viewBox="0 0 14 9"
fill="none"
className={styles.icon}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.75593 8.12713C7.35716 8.58759 6.64284 8.58759 6.24407 8.12713L0.638743 1.65465C0.0778675 1.00701 0.537921 0 1.39467 0L12.6053 0C13.4621 0 13.9221 1.00701 13.3613 1.65465L7.75593 8.12713Z"
fill="#2A2A62"
/>
</svg>
);
}

View File

@ -1,7 +1,7 @@
.card {
padding: calc(30rem / 16) calc(40rem / 16);
max-width: calc(524rem / 16);
background-color: var(--primary-background);
background-color: var(--card-background);
border-radius: calc(20rem / 16);
margin-bottom: 1rem;
}
@ -10,15 +10,17 @@
max-width: unset;
padding: unset;
border-radius: unset;
background-color: var(--primary-background);
}
.date {
font-size: calc(18rem / 16);
margin: 0;
color: var(--primary-subtitle);
}
.author {
color: var(--secondary-heading);
color: var(--author-text);
font-style: normal;
}

View File

@ -30,7 +30,7 @@
margin: calc(8rem / 16) calc(32rem / 16) calc(20rem / 16) 0;
height: calc(100vh - (44rem / 16));
color: var(--primary-heading);
color: var(--sidebar-text);
font-weight: 500;
}
@ -55,10 +55,14 @@
.selected {
background-color: var(--primary-accent-lightest);
color: var(--primary-accent);
color: var(--primary-heading);
font-weight: 700;
}
.selected div {
color: var(--primary-heading);
}
.readAll {
font-weight: 700;
}
@ -69,7 +73,7 @@
.selected .marker {
display: inline;
background-color: var(--primary-accent);
background-color: var(--marker);
height: calc(24rem / 16);
width: calc(4rem / 16);
margin-right: 1rem;

View File

@ -8,12 +8,12 @@
flex-direction: row;
align-items: flex-end;
padding-bottom: 1rem;
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
}
.header {
line-height: 1;
color: var(--primary-heading);
color: var(--primary-title);
font-size: calc(48rem / 16);
margin: 0 0 0 calc(36rem / 16);
text-align: center;

View File

@ -22,5 +22,5 @@
}
.white {
fill: var(--primary-background);
fill: var(--text-light);
}

View File

@ -77,7 +77,7 @@ function InstagramSvg(color: string) {
xmlns="http://www.w3.org/2000/svg"
>
<linearGradient id="bluegreen-gradient">
<stop offset="0%" stopColor="#1481E3" />
<stop offset="0%" stopColor="var(--blue-gradient)" />
<stop offset="100%" stopColor="#4ED4B2" />
</linearGradient>
<path
@ -101,7 +101,7 @@ function DiscordSvg(color: string) {
version="1.1"
>
<linearGradient id="bluegreen-gradient">
<stop offset="0%" stopColor="#1481E3" />
<stop offset="0%" stopColor="var(--blue-gradient)" />
<stop offset="100%" stopColor="#4ED4B2" />
</linearGradient>
<g id="surface1">
@ -125,7 +125,7 @@ function TwitchSvg(color: string) {
xmlns="http://www.w3.org/2000/svg"
>
<linearGradient id="bluegreen-gradient">
<stop offset="0%" stopColor="#1481E3" />
<stop offset="0%" stopColor="var(--blue-gradient)" />
<stop offset="100%" stopColor="#4ED4B2" />
</linearGradient>
<g clipPath="url(#clip0)">
@ -170,7 +170,7 @@ function FacebookSvg(color: string) {
xmlns="http://www.w3.org/2000/svg"
>
<linearGradient id="bluegreen-gradient">
<stop offset="0%" stopColor="#1481E3" />
<stop offset="0%" stopColor="var(--blue-gradient)" />
<stop offset="100%" stopColor="#4ED4B2" />
</linearGradient>
<path
@ -194,7 +194,7 @@ function LiberaSvg(color: string) {
xmlns="http://www.w3.org/2000/svg"
>
<linearGradient id="bluegreen-gradient">
<stop offset="0%" stopColor="#1481E3" />
<stop offset="0%" stopColor="var(--blue-gradient)" />
<stop offset="100%" stopColor="#4ED4B2" />
</linearGradient>
<path

View File

@ -7,7 +7,7 @@
}
.table thead tr {
background: var(--secondary-accent-light);
background: var(--table-header);
}
.table tbody tr {
@ -15,8 +15,8 @@
vertical-align: top;
}
.table tbody tr:nth-child(odd) {
background: var(--primary-accent-lightest);
.table tbody tr:nth-child(even) {
background: var(--table-section);
}
.table th {

View File

@ -29,9 +29,17 @@ export const PALETTE_NAMES = [
"--secondary-accent-light",
"--primary-heading",
"--secondary-heading",
"--primary-title",
"--primary-subtitle",
"--secondary-subtitle",
"--primary-text",
"--text",
"--text-light",
"--author-text",
"--sidebar-text",
"--mini-event-card-text",
"--form-invalid",
"--warning-background",
@ -41,9 +49,24 @@ export const PALETTE_NAMES = [
"--input-placeholder-text",
"--input-text",
"--icon",
"--code-background",
"--button-background",
"--footer-background",
"--card-background",
"--dark-card-background",
"--table-header",
"--table-section",
"--navbar-page-overlay",
"--link",
"--link-hover",
"--blue-gradient",
"--border",
"--marker",
] as const;
export const emptyPalette = PALETTE_NAMES.reduce(
@ -77,15 +100,9 @@ export function ThemeProvider(props: Props) {
const setTheme = (input: SetThemeInput) => {
if (typeof input === "string") {
PALETTE_NAMES.forEach((name) =>
document.body.style.setProperty(name, "")
document.body.style.setProperty(name, "var(--" + input + name + ")")
);
if (input === "light") {
document.body.classList.remove("dark");
} else if (input === "dark") {
document.body.classList.add("dark");
}
savePalette(getCurrentPalette(input));
setThemeName(input);
} else {
const properties = Object.keys(input) as PaletteNames[];
@ -107,7 +124,14 @@ export function ThemeProvider(props: Props) {
const customPalette = getSavedPalette();
if (customPalette == null) {
setThemeName("light");
const prefersDark = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
if (prefersDark) {
setTheme("dark");
} else {
setTheme("light");
}
} else {
setTheme(customPalette);
setThemeName("custom");
@ -124,11 +148,11 @@ export function ThemeProvider(props: Props) {
theme: {
name: themeName,
get palette() {
return getCurrentPalette();
return getCurrentPalette(themeName);
},
},
setTheme,
save: () => savePalette(getCurrentPalette()),
save: () => savePalette(getCurrentPalette(themeName)),
clearSaved: clearSavedPalette,
}
}
@ -148,13 +172,30 @@ export type Palette = {
[key in PaletteNames]: string;
};
function getCurrentPalette() {
const styles = getComputedStyle(document.body);
function themePropertyName(
name: PaletteNames,
themeName: BuiltInThemes
): string {
return `--${themeName}${name}`;
}
function getCurrentPalette(themeName: string) {
const styles = getComputedStyle(document.body);
if (themeName === "light" || themeName === "dark") {
return PALETTE_NAMES.reduce(
(partial, name) => ({
...partial,
[name]: styles
.getPropertyValue(themePropertyName(name, themeName))
.trim(),
}),
{} as Palette
);
}
return PALETTE_NAMES.reduce(
(partial, varName) => ({
(partial, name) => ({
...partial,
[varName]: styles.getPropertyValue(varName).trim(),
[name]: styles.getPropertyValue(name).trim(),
}),
{} as Palette
);

View File

@ -1,16 +1,16 @@
---
name: 'Bond-fire'
short: "Join CSC and WICS for a bonfire with s'mores and snacks, on us!"
startDate: 'May 27 2022 19:00'
endDate: 'May 27 2022 22:00'
startDate: 'May 29 2022 19:00'
endDate: 'May 29 2022 22:00'
online: false
location: 'Laurel Creek Fire Pit'
poster: 'images/events/2022/spring/Bondfire.png'
registerLink: https://bit.ly/s22-bonfire-signup
---
Would you like to meet new friends and talk with like-minded individuals in person? 🔥 Come join CSC and WICS for a bonfire outside at the Laurel Creek firepit (across Ring. Rd. from EV3) on May 27th 2022 from 7:00pm-10:00pm EST, and enjoy a wonderful time with lots of pizza and snacks! 🍕 The firepit location will be in the application form. Feel free to bring your own roasting skewer and instruments to jam along to 🤘
Would you like to meet new friends and talk with like-minded individuals in person? 🔥 Come join CSC and WICS for a bonfire outside at the Laurel Creek firepit (across Ring. Rd. from EV3) on May 29th 2022 from 7:00pm-10:00pm EST, and enjoy a wonderful time with lots of pizza and snacks! 🍕 The firepit location will be in the application form. Feel free to bring your own roasting skewer and instruments to jam along to 🤘
👉Sign up at https://bit.ly/s22-bonfire-signup or email us at exec@csclub.uwaterloo.ca!
📅 Deadline to sign up: May 25th, 2022 at 11:59 PM EST.
📅 Deadline to sign up: May 27th, 2022 at 11:59 PM EST.

View File

@ -0,0 +1,24 @@
---
name: 'Code Party and Midterm Scream'
short: 'Come hang out and work on homework or side-projects at our first code party of this term! Featuring snacks and a stress relieving synchronized scream.'
startDate: 'June 17 2022 19:00'
endDate: 'June 17 2022 21:00'
online: false
location: 'STC 0060'
poster: 'images/events/2022/spring/Code-Party-Scream.png'
registerLink: https://bit.ly/s22-midtermscream-codeparty-signup
---
📣Do you ever feel like screaming on the top of your lungs? 🙀 Then CSCs Code Party and Midterm Scream is where you need to be!
🤩 Come to our first code party of the term! You can chill out, work on side-projects, or finish up on homework and assignments. 📚 There will also be snacks while you are working away or just hanging out with a fellow CSC friend. At 9 PM we will head out to the Rock Garden to let out a synchronized, one-minute long scream to relieve midterm stress! 🥳
Code Party:
📆 Event Date: June 17th from 7 - 9 PM
📌 Location: STC 0060
🙀 The Midterm Scream will take place at 9 PM in the Rock Garden!
👉 Sign-up through this link: https://bit.ly/s22-midtermscream-codeparty-signup
We hope to see you there!

View File

@ -0,0 +1,22 @@
---
name: 'CSC: Soiree: Movie Night'
short: "Bring your friends for this chill movie night with CSC!"
startDate: 'June 10 2022 19:00'
endDate: 'June 10 2022 22:00'
online: false
location: 'AL 116'
poster: 'images/events/2022/spring/MovieNight.png'
registerLink: https://bit.ly/s22-movienight-signup
---
📣 Looking to destress before midterm week starts? ✨CSC is hosting the perfect event for you! Come hang out at CSC Soiree: Movie Night! 🎬
🍿Bring your friends for this chill movie night from 7-10 PM! 👀 There will be time for a mini social afterwards so make sure to come if you want to meet other CSC members!
📆 Event Date: June 10 from 7 PM - 10 PM
📌 Location: AL 116
👉 Sign-up through this link: https://bit.ly/s22-movienight-signup
We cant wait to see you there!

View File

@ -0,0 +1,8 @@
---
title: Cloud Accounts
index: 2
---
With the CSC cloud, you can create your own virtual machines, host your Docker container images, and deploy your apps on Kubernetes.
See [https://docs.cloud.csclub.uwaterloo.ca/](https://docs.cloud.csclub.uwaterloo.ca/) for more details.

View File

@ -1,6 +1,6 @@
---
title: Club Web Hosting
index: 4
index: 5
---
If you're a club and looking for web space, the CS Club is the place go.

View File

@ -1,6 +1,6 @@
---
title: CS Club Email
index: 2
index: 3
---
Members also receive a **[username@csclub.uwaterloo.ca](#)** email address.

View File

@ -1,6 +1,6 @@
---
title: Git Hosting
index: 5
index: 6
---
We host an instance of [Gitea](https://git.csclub.uwaterloo.ca/) for all of our members. The UI and workflow of Gitea are very similar to GitHub. Gitea is currently home to most CSC software projects, including the [code for this website](https://git.csclub.uwaterloo.ca/www/www-new).

View File

@ -1,6 +1,6 @@
---
title: In-Office Books
index: 11
index: 12
---
The CS Club maintains an extensive collection of Computer Science-related books. Feel free to come by the office to take a look at our library.

View File

@ -1,6 +1,6 @@
---
title: IRC
index: 7
index: 8
---
We host an instance of [The Lounge](https://chat.csclub.uwaterloo.ca/) for all of our members. The Lounge is a web-based IRC client which is simple to setup and use. It also has a Progressive Web App available for mobile devices.

View File

@ -1,6 +1,6 @@
---
title: Live Streaming
index: 10
index: 11
---
We host an instance of [Icecast](https://icy.csclub.uwaterloo.ca/), which can stream live audio and video. We have successfully streamed live events to Icecast using OBS Studio. Latency usually ranges between 5-10 sec.

View File

@ -1,6 +1,6 @@
---
title: Mailing Lists
index: 12
index: 13
---
Our [csc-general mailing list](https://mailman.csclub.uwaterloo.ca/postorius/lists/csc-general.csclub.uwaterloo.ca/) informs members about our current events.

View File

@ -1,6 +1,6 @@
---
title: Mattermost
index: 8
index: 9
---
We host an instance of [Mattermost](https://mattermost.csclub.uwaterloo.ca/) for all of our members. Mattermost is an open-source alternative to Slack. We currently bridge the `#csc` channel on libera.chat to Mattermost (if you are looking for a generic web-based IRC client, see [The Lounge](/resources/services/irc)). CSC executives currently use Mattermost for planning and logistics.

View File

@ -1,6 +1,6 @@
---
title: Software Mirror
index: 6
index: 7
---
The CSC runs a mirror of popular open source software. The [mirror](http://mirror.csclub.uwaterloo.ca/) has a list of available software. More information is available on our [wiki article](http://wiki.csclub.uwaterloo.ca/Mirror).

View File

@ -1,6 +1,6 @@
---
title: SSH Key Fingerprints
index: 13
index: 14
---
| Machine Name | Key Type | Fingerprint |

View File

@ -1,6 +1,6 @@
---
title: Video Conferencing
index: 9
index: 10
---
We host an instance of [BigBlueButton](https://bbb.csclub.uwaterloo.ca/), a free and open-source video conferencing platform. BigBlueButton offers many useful features such as a multi-user whiteboard, breakout rooms, shared notes, and more.

View File

@ -1,6 +1,6 @@
---
title: Web Hosting
index: 3
index: 4
---
Many of members take advantage of our web hosting service. Our web server runs on Apache, and has PHP, Python, and Perl modules installed. We also have MySQL and PostgreSQL databases available upon request.

View File

@ -0,0 +1,6 @@
---
name: Haley Song
role: Assistant Vice-President
---
Hello! I'm Haley, and I'm currently in my 2B term of Computer Science. As a Marketing Lead and Assistant Vice President of CSC this term, I hope to spread more positive energy in CSC and make more people feel welcomed in the CS community. I love to see a lot of people enjoying their time at our events 😄 . My hobbies include going out for a long walk for some fresh air. Feel free to reach out to me for any questions or concerns!

View File

@ -3,4 +3,4 @@ name: Raymond Li
role: Systems Administrator
---
Hey there! I'm [Raymond Li](https://raymond.li). I'm in my 2B CS term. As sysadmin, I lead syscom in maintaining all the club infrastructure, including our [machines](https://wiki.csclub.uwaterloo.ca/Machine_List), [mirror](https://mirror.csclub.uwaterloo.ca/stats), [wiki](https://wiki.csclub.uwaterloo.ca), [email service](https://mail.csclub.uwaterloo.ca), [Gitea instance](https://git.csclub.uwaterloo.ca), [Mattermost instance](https://mattermost.csclub.uwaterloo.ca), [TheLounge instance](https://chat.csclub.uwaterloo.ca), and much much more. I like to code, ski, and play chess and violin.<br />#btwiusearch (at least until [Linux expires in 2038](https://youtu.be/dQw4w9WgXcQ))
Hey there! I'm [Raymond Li](https://raymond.li). I'm on co-op after my 2B term of CS. As sysadmin, I lead syscom and termcom in maintaining all the club infrastructure, including our [machines](https://wiki.csclub.uwaterloo.ca/Machine_List), [mirror](https://mirror.csclub.uwaterloo.ca/stats), [wiki](https://wiki.csclub.uwaterloo.ca), [email service](https://mail.csclub.uwaterloo.ca), [Gitea instance](https://git.csclub.uwaterloo.ca), [Mattermost instance](https://mattermost.csclub.uwaterloo.ca), [TheLounge instance](https://chat.csclub.uwaterloo.ca), and much much more. Major projects I've got planned this term include setting up NextCloud and our own self-hosted URL shortener at csclub.ca! In my spare time, I like to code, ski, and play chess and violin.<br />#btwiusearch (at least until [Linux expires in 2038](https://youtu.be/dQw4w9WgXcQ))

View File

@ -8,5 +8,10 @@
"startDate": "January 29 2022 21:00",
"endDate": "January 30 2022 18:00",
"message": "This is a sample warning"
},
{
"startDate": "June 13 2022 00:00",
"endDate": "June 15 2022 22:00",
"message": "CSC machines will have their software updated and will momentarily go offline at 6pm EST on Wednesday, June 15, 2022"
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

View File

@ -88,7 +88,7 @@ export async function getNewsBySlug(
metadata: {
...metadata,
date: getLocalDateFromEST(
parse(metadata.date, DATE_FORMAT, new Date())
parse(metadata.date as string, DATE_FORMAT, new Date())
).toString(),
permalink: `/news/${year}/${term}/${slugDate}`,
} as Metadata,

View File

@ -96,7 +96,7 @@ export async function getExec(name: string, pos: string, convert = true) {
const raw = await readFile(path.join(EXECS_PATH, `${name}${FILETYPE}`));
({ content, data: metadata } = matter(raw));
const image = await getMemberImagePath(metadata.name);
const image = await getMemberImagePath(metadata.name as string);
return {
content: convert ? await serialize(content) : content,

3162
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -41,14 +41,14 @@
"@types/mdx-js__react": "^1.5.3",
"@types/node": "^16.9.1",
"@types/react": "^17.0.14",
"@typescript-eslint/eslint-plugin": "4.28.4",
"@typescript-eslint/parser": "4.28.4",
"eslint": "7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.24.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"@typescript-eslint/eslint-plugin": "5.22.0",
"@typescript-eslint/parser": "5.22.0",
"eslint": "8.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "7.29.4",
"eslint-plugin-react-hooks": "^4.5.0",
"gray-matter": "^4.0.3",
"ical-generator": "^3.0.0",
"postcss": "^8.3.0",
@ -56,6 +56,6 @@
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-preset-env": "^7.0.0",
"ts-node": "^10.2.1",
"typescript": "4.3.5"
"typescript": "4.6.4"
}
}

View File

@ -4,34 +4,161 @@ html {
body {
/* Default is light theme */
--primary-background: #ffffff;
--secondary-background: #fdf8f5;
--light--primary-background: #ffffff;
--light--secondary-background: #fdf8f5;
--primary-accent: #1482e3;
--primary-accent-soft: #5caff9;
--primary-accent-light: #c4e0f8;
--primary-accent-lighter: #e1eefa;
--primary-accent-lightest: #f7fbff;
--light--primary-accent: #1482e3;
--light--primary-accent-soft: #5caff9;
--light--primary-accent-light: #c4e0f8;
--light--primary-accent-lighter: #e1eefa;
--light--primary-accent-lightest: #f7fbff;
--secondary-accent: #4ed4b2;
--secondary-accent-light: #dcf6f0;
--light--secondary-accent: #4ed4b2;
--light--secondary-accent-light: #dcf6f0;
--primary-heading: #2a2a62;
--secondary-heading: #525284;
--light--primary-heading: #2a2a62;
--text: #000000;
--light--primary-text: #2a2a62;
--light--text: #000000;
--light--text-light: #FFFFFF;
--light--sidebar-text: #2a2a62;
--light--author-text: #525284;
--light--mini-event-card-text: #000000;
--form-invalid: #9f616a;
--warning-background: #dd0014;
--warning-text: #ffffff;
--light--primary-title: #2a2a62;
--light--primary-subtitle: #2a2a62;
--light--secondary-subtitle: #2a2a62;
--input-background: #f0f0f0;
--input-placeholder-text: #bbbbbb;
--input-text: #6b6b6b;
--light--form-invalid: #9f616a;
--light--warning-background: #dd0014;
--light--warning-text: #ffffff;
--code-background: #f0f0f0;
--light--link: #1482e3;
--light--link-hover: #4ed3b2;
--navbar-page-overlay: #787878b2;
--light--input-background: #f0f0f0;
--light--input-placeholder-text: #bbbbbb;
--light--input-text: #6b6b6b;
--light--icon: #2A2A62;
--light--navbar-page-overlay: hsla(0,0%,47.1%,0.6980392156862745);
--light--code-background: #f0f0f0;
--light--button-background: #1482e3;
--light--footer-background: #2a2a62;
--light--card-background: #FFFFFF;
--light--dark-card-background: #DCF6F0;
--light--table-header: #DCF6F0;
--light--table-section: #f7fbff;
--light--blue-gradient: #1481E3;
--light--border: #2a2a62;
--light--marker: #1482e3;
--dark--primary-background: #171729;
--dark--secondary-background: #252542;
--dark--primary-accent: #5CAFF9;
--dark--primary-accent-soft: #5cb0f9;
--dark--primary-accent-light: #252542;
--dark--primary-accent-lighter: #171729;
--dark--primary-accent-lightest: #464671;
--dark--secondary-accent: #4ed3b2;
--dark--secondary-accent-light: #525284;
--dark--primary-heading: #fcf7f4;
--dark--form-invalid: #9f616a;
--dark--warning-background: #dd0014;
--dark--warning-text: #ffffff;
--dark--link: #4ED4B2;
--dark--link-hover: #C1F0E4;
--dark--primary-text: #ffffff;
--dark--text: #AFAFD3;
--dark--text-light: #FFFFFF;
--dark--sidebar-text: #AFAFD3;
--dark--author-text: #ABABF2;
--dark--mini-event-card-text: #5CAFF9;
--dark--input-background: #C4C4C4;
--dark--input-placeholder-text: #bbbbbb;
--dark--input-text: #6b6b6b;
--dark--icon: #ffffff;
--dark--primary-title: #5CAFF9;
--dark--primary-subtitle: #E0E0F7;
--dark--secondary-subtitle: #ABABF2;
--dark--navbar-page-overlay: rgba(0, 0, 0, 0.7);
--dark--code-background: #3d3d68;
--dark--button-background: #1482e3;
--dark--footer-background: #525284;
--dark--card-background: #272751;
--dark--dark-card-background: #272751;
--dark--table-header: #252542;
--dark--table-section: #202037;
--dark--blue-gradient: #5CAFF9;
--dark--border: #2A2A62;
--dark--marker: #C5C5F0;
--primary-background: var(--light--primary-background);
--secondary-background: var(--light--secondary-background);
--primary-accent: var(--light--primary-accent);
--primary-accent-soft: var(--light--primary-accent-soft);
--primary-accent-light: var(--light--primary-accent-light);
--primary-accent-lighter: var(--light--primary-accent-lighter);
--primary-accent-lightest: var(--light--primary-accent-lightest);
--secondary-accent: var(--light--secondary-accent);
--secondary-accent-light: var(--light--secondary-accent-light);
--primary-heading: var(--light--primary-heading);
--form-invalid: var(--light--form-invalid);
--warning-background: var(--light--warning-background);
--warning-text: var(--light--warning-text);
--link: var(--light--link);
--link-hover: var(--light--link-hover);
--primary-text: var(--light--primary-text);
--text: var(--light--text);
--text-light: var(--light--text-light);
--sidebar-text: var(--light--sidebar-text);
--author-text: var(--light--author-text);
--mini-event-card-text: var(--light--mini-event-card-text);
--input-background: var(--light--input-background);
--input-placeholder-text: var(--light--input-placeholder-text);
--input-text: var(--light--input-text);
--icon: var(--light--icon);
--primary-title: var(--light--primary-title);
--primary-subtitle: var(--light--primary-subtitle);
--secondary-subtitle: var(--light--secondary-subtitle);
--navbar-page-overlay: var(--light--navbar-page-overlay);
--code-background: var(--light--code-background);
--button-background: var(--light--button-background);
--footer-background: var(--light--footer-background);
--card-background: var(--light--card-background);
--dark-card-background: var(--light--dark-card-background);
--table-header: var(--light--table-header);
--table-section: var(--light--table-section);
--blue-gradient: var(--light--blue-gradient);
--border: var(--light--border);
--marker: var(--light--marker);
background-color: var(--primary-background);
color: var(--text);
@ -47,10 +174,6 @@ input {
font-family: "Poppins", "sans-serif";
}
.dark {
/* FIXME: Implement dark theme */
}
h1,
h2,
h3,
@ -117,3 +240,58 @@ h4 + * {
font-weight: 600;
}
}
@media only screen and (prefers-color-scheme: dark) {
body {
--primary-background: var(--dark--primary-background);
--secondary-background: var(--dark--secondary-background);
--primary-accent: var(--dark--primary-accent);
--primary-accent-soft: var(--dark--primary-accent-soft);
--primary-accent-light: var(--dark--primary-accent-light);
--primary-accent-lighter: var(--dark--primary-accent-lighter);
--primary-accent-lightest: var(--dark--primary-accent-lightest);
--secondary-accent: var(--dark--secondary-accent);
--secondary-accent-light: var(--dark--secondary-accent-light);
--primary-heading: var(--dark--primary-heading);
--form-invalid: var(--dark--form-invalid);
--warning-background: var(--dark--warning-background);
--warning-text: var(--dark--warning-text);
--link: var(--dark--link);
--link-hover: var(--dark--link-hover);
--primary-text: var(--dark--primary-text);
--text: var(--dark--text);
--text-light: var(--dark--text-light);
--sidebar-text: var(--dark--sidebar-text);
--author-text: var(--dark--author-text);
--mini-event-card-text: var(--dark--mini-event-card-text);
--input-background: var(--dark--input-background);
--input-placeholder-text: var(--dark--input-placeholder-text);
--input-text: var(--dark--input-text);
--icon: var(--dark--icon);
--primary-title: var(--dark--primary-title);
--primary-subtitle: var(--dark--primary-subtitle);
--secondary-subtitle: var(--dark--secondary-subtitle);
--navbar-page-overlay: var(--dark--navbar-page-overlay);
--code-background: var(--dark--code-background);
--button-background: var(--dark--button-background);
--footer-background: var(--dark--footer-background);
--card-background: var(--dark--card-background);
--table-header: var(--dark--table-header);
--table-section: var(--dark--table-section);
--blue-gradient: var(--dark--blue-gradient);
--border: var(--dark--border);
--marker: var(--dark--marker);
}
}

View File

@ -6,6 +6,7 @@
height: calc(80rem / 16);
margin-top: auto;
padding-left: calc(20rem / 16);
color: var(--primary-title);
}
.content {

View File

@ -1,6 +1,14 @@
.title {
color: var(--primary-title);
}
.table {
display: table;
width: 100%;
margin-top: calc(20rem / 16);
margin-bottom: calc(60rem / 16);
}
.table thead {
background-color: var(--table-header);
}

View File

@ -20,7 +20,7 @@ export default function Members(props: Props) {
return (
<>
<Title>Members</Title>
<h1>Members</h1>
<h1 className={styles.title}>Members</h1>
<p>
{`The members for ${capitalize(props.term)} ${
props.year

View File

@ -7,11 +7,11 @@
flex-direction: row;
align-items: flex-end;
padding-bottom: 1rem;
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
}
.header {
color: var(--primary-heading);
color: var(--primary-title);
margin: 0 1rem 0 0;
text-align: center;
}

View File

@ -2,7 +2,7 @@
display: flex;
flex-direction: row;
padding-bottom: calc(24rem / 16);
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
margin-bottom: calc(46rem / 16);
}
@ -15,7 +15,7 @@
}
.header {
color: var(--primary-heading);
color: var(--primary-title);
font-size: calc(48rem / 16);
margin: 0 calc(53rem / 16) 0 0;
}
@ -25,7 +25,7 @@
font-size: calc(36rem / 16);
font-weight: 600;
padding-bottom: calc(22rem / 16);
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
margin-bottom: calc(46rem / 16);
margin-top: calc(86rem / 16);
}

View File

@ -4,8 +4,9 @@
.main > h1,
.main > section > h1 {
color: var(--primary-title);
padding-bottom: calc(16rem / 16);
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
}
.header {

View File

@ -99,15 +99,18 @@ export default function TermPage(props: Props) {
</h1>
)}
<div className={styles.miniEventCards}>
{props.pastEvents.map(({ content, metadata }) => (
{props.pastEvents.map((event, idx) => (
<MiniEventCard
{...metadata}
startDate={new Date(metadata.startDate)}
{...event.metadata}
startDate={new Date(event.metadata.startDate)}
endDate={
metadata.endDate ? new Date(metadata.endDate) : undefined
event.metadata.endDate
? new Date(event.metadata.endDate)
: undefined
}
description={<MDXRemote {...content} />}
key={metadata.name + metadata.startDate.toString()}
description={<MDXRemote {...event.content} />}
key={event.metadata.name + event.metadata.startDate.toString()}
background={idx % 2 === 0 ? "dark-bg" : "normal-bg"}
/>
))}
</div>

View File

@ -4,7 +4,7 @@
.main > h1 {
padding-bottom: calc(16rem / 16);
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
}
.blue {

View File

@ -5,11 +5,11 @@
.page > header {
display: flex;
flex-direction: row;
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
}
.headerText > h1 {
color: var(--primary-heading);
color: var(--primary-title);
margin: 0 0 calc(8rem / 16) 0;
}

View File

@ -56,7 +56,7 @@
.clubDescription {
margin-bottom: calc(50rem / 16);
text-align: center;
color: var(--primary-heading);
color: var(--primary-text);
}
.clubDescription br {

View File

@ -4,7 +4,7 @@
.page > h1 {
padding-bottom: calc(16rem / 16);
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
}
.term {

View File

@ -4,11 +4,12 @@
justify-content: space-between;
align-items: flex-end;
padding-bottom: calc(32rem / 16);
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
}
.title {
padding-right: calc(40rem / 16);
color: var(--primary-title);
}
.adviceBarContainer {
@ -30,10 +31,22 @@ a.currentAdvice {
color: var(--primary-accent);
}
.adviceBarContainer a:hover {
color: var(--link-hover);
}
.content {
padding-bottom: calc(48rem / 16);
}
.content h3 {
color: var(--primary-subtitle);
}
.content h4 {
color: var(--secondary-subtitle);
}
@media only screen and (max-width: calc(768rem / 16)) {
.titleContainer {
flex-direction: column-reverse;

View File

@ -8,7 +8,7 @@
align-items: flex-end;
padding-bottom: 1rem;
margin-bottom: calc(40rem / 16);
border-bottom: calc(1rem / 16) solid var(--primary-heading);
border-bottom: calc(1rem / 16) solid var(--border);
}
.header {
@ -16,7 +16,7 @@
}
.header h1 {
color: var(--primary-heading);
color: var(--primary-title);
margin: 0 1rem 0 0;
text-align: left;
}

View File

@ -26,6 +26,15 @@ export default function Themer() {
>
Reset to light mode
</Button>
<Button
type="reset"
onClick={() => {
context?.clearSaved();
context?.setTheme("dark");
}}
>
Reset to dark mode
</Button>
<Button type="submit" onClick={() => context?.save()}>
Save
</Button>

View File

@ -16,14 +16,16 @@ import { default as getImageDimensions } from "image-size";
const SOURCE_DIRECTORY = "images";
const DESTINATION_DIRECTORY = path.join("public", "images");
// directory where Meet the Team headshots are stored, relative to the source directory
// directories are relative to SOURCE_DIRECTORY
const TEAM_IMAGES_DIRECTORY = path.join("team", "");
const EVENTS_IMAGES_DIRECTORY = path.join("events", "");
const IMAGE_MINIMUM_SIZE = 512;
const GET_ENCODER_FROM_EXTENSION: { [imageExtension: string]: string } = {
jpg: "mozjpeg",
jpeg: "mozjpeg",
JPG: "mozjpeg",
png: "oxipng",
};
@ -35,6 +37,8 @@ const ENCODER_OPTIONS: { [encoder: string]: Record<string, unknown> } = {
void optimizeImages();
export async function optimizeImages() {
const startTime = Date.now();
const imagePaths = await getFilePathsInDirectory(SOURCE_DIRECTORY);
await fse.emptyDir(DESTINATION_DIRECTORY);
@ -42,52 +46,73 @@ export async function optimizeImages() {
const numberOfWorkers = Math.min(cpus().length, 8);
const imagePool = new ImagePool(numberOfWorkers);
await Promise.all(
imagePaths.map(async (imagePath) => {
const sourcePath = path.join(SOURCE_DIRECTORY, imagePath);
const destinationPath = path.join(DESTINATION_DIRECTORY, imagePath);
const fileExtension = imagePath.split(".").pop() ?? "";
const encoder = GET_ENCODER_FROM_EXTENSION[fileExtension];
// process smaller batches in order to reduce memory usage
const batchSize = 32;
if (!encoder) {
await fse.copy(sourcePath, destinationPath);
return;
}
for (let i = 0; i < imagePaths.length; i += batchSize) {
await Promise.all(
imagePaths.slice(i, i + batchSize).map(async (imagePath) => {
const imageStartTime = Date.now();
const rawImageFile = await fse.readFile(sourcePath);
const ingestedImage = imagePool.ingestImage(rawImageFile);
const { width, height } = getImageDimensions(rawImageFile);
const sourcePath = path.join(SOURCE_DIRECTORY, imagePath);
const destinationPath = path.join(DESTINATION_DIRECTORY, imagePath);
const fileExtension = imagePath.split(".").pop() ?? "";
const encoder = GET_ENCODER_FROM_EXTENSION[fileExtension];
await ingestedImage.decoded;
if (!encoder) {
await fse.copy(sourcePath, destinationPath);
console.log(
`Copied ${imagePath} in ${getElapsedSeconds(imageStartTime)}s`
);
return;
}
const shouldResize =
imagePath.startsWith(TEAM_IMAGES_DIRECTORY) &&
(width ?? 0) > IMAGE_MINIMUM_SIZE &&
(height ?? 0) > IMAGE_MINIMUM_SIZE;
const rawImageFile = await fse.readFile(sourcePath);
const ingestedImage = imagePool.ingestImage(rawImageFile);
const { width, height } = getImageDimensions(rawImageFile);
if (width && height && shouldResize) {
const smallerDimension = width < height ? "width" : "height";
await ingestedImage.decoded;
// specifying only one dimension maintains the aspect ratio
const preprocessOptions = {
resize: {
enabled: true,
[smallerDimension]: IMAGE_MINIMUM_SIZE,
},
};
const shouldResize =
(imagePath.startsWith(TEAM_IMAGES_DIRECTORY) ||
imagePath.startsWith(EVENTS_IMAGES_DIRECTORY)) &&
(width ?? 0) > IMAGE_MINIMUM_SIZE &&
(height ?? 0) > IMAGE_MINIMUM_SIZE;
await ingestedImage.preprocess(preprocessOptions);
}
if (width && height && shouldResize) {
const smallerDimension = width < height ? "width" : "height";
const encodeOptions = { [encoder]: ENCODER_OPTIONS[encoder] };
await ingestedImage.encode(encodeOptions);
// specifying only one dimension maintains the aspect ratio
const preprocessOptions = {
resize: {
enabled: true,
[smallerDimension]: IMAGE_MINIMUM_SIZE,
},
};
const encodedImage = await ingestedImage.encodedWith[encoder];
await fse.outputFile(destinationPath, encodedImage.binary);
})
);
await ingestedImage.preprocess(preprocessOptions);
console.log(
`Resized ${sourcePath} in ${getElapsedSeconds(imageStartTime)}s`
);
}
const encodeOptions = { [encoder]: ENCODER_OPTIONS[encoder] };
await ingestedImage.encode(encodeOptions);
const encodedImage = await ingestedImage.encodedWith[encoder];
await fse.outputFile(destinationPath, encodedImage.binary);
console.log(
`Optimized ${sourcePath} in ${getElapsedSeconds(imageStartTime)}s`
);
})
);
}
await imagePool.close();
console.log(`TOTAL DURATION: ${getElapsedSeconds(startTime)}s`);
}
async function getFilePathsInDirectory(directory: string): Promise<string[]> {
@ -105,3 +130,7 @@ async function getFilePathsInDirectory(directory: string): Promise<string[]> {
)
).flat();
}
function getElapsedSeconds(startTime: number) {
return (Date.now() - startTime) / 1000;
}