Implement dark theme (Closes #287) (#407)
continuous-integration/drone/push Build is passing Details

* The website will have a dark theme or a light theme depending on your OS preferences. You can still customize in the `themer` page to override some properties.
* A toggle button in the footer to toggle between light mode and dark mode.
* Added a "Reset to dark mode" button in the `themer` page.

Staging link: https://csclub.uwaterloo.ca/~a3thakra/csc/dark-theme/

Co-authored-by: Miniapple8888 <miniapple8888@gmail.com>
Reviewed-on: #407
Reviewed-by: Amy <a258wang@csclub.uwaterloo.ca>
This commit is contained in:
Mark Chiu 2022-06-08 08:45:28 -04:00
parent 443925190e
commit f631f4013f
37 changed files with 1675 additions and 2046 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -120,7 +120,7 @@ export function Navbar() {
className={styles.hamburger} className={styles.hamburger}
onClick={() => dispatch({ type: "open", route: router.pathname })} onClick={() => dispatch({ type: "open", route: router.pathname })}
> >
<Image src="/images/hamburger.svg" alt="Menu" /> <HamburgerSvg />
</button> </button>
<div <div
className={ className={
@ -263,7 +263,7 @@ function NavItem(props: NavItemProps) {
} }
onClick={() => props.onToggle(props.route)} onClick={() => props.onToggle(props.route)}
> >
<Image src="/images/dropdown-icon.svg" alt="Dropdown Icon" /> <DropdownSvg />
</button> </button>
<ul <ul
className={ className={
@ -348,3 +348,64 @@ function getMainRoute(route: string) {
} }
return "/" + route.split("/")[1]; 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 { .card {
padding: calc(30rem / 16) calc(40rem / 16); padding: calc(30rem / 16) calc(40rem / 16);
max-width: calc(524rem / 16); max-width: calc(524rem / 16);
background-color: var(--primary-background); background-color: var(--card-background);
border-radius: calc(20rem / 16); border-radius: calc(20rem / 16);
margin-bottom: 1rem; margin-bottom: 1rem;
} }
@ -10,15 +10,17 @@
max-width: unset; max-width: unset;
padding: unset; padding: unset;
border-radius: unset; border-radius: unset;
background-color: var(--primary-background);
} }
.date { .date {
font-size: calc(18rem / 16); font-size: calc(18rem / 16);
margin: 0; margin: 0;
color: var(--primary-subtitle);
} }
.author { .author {
color: var(--secondary-heading); color: var(--author-text);
font-style: normal; font-style: normal;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -88,7 +88,7 @@ export async function getNewsBySlug(
metadata: { metadata: {
...metadata, ...metadata,
date: getLocalDateFromEST( date: getLocalDateFromEST(
parse(metadata.date, DATE_FORMAT, new Date()) parse(metadata.date as string, DATE_FORMAT, new Date())
).toString(), ).toString(),
permalink: `/news/${year}/${term}/${slugDate}`, permalink: `/news/${year}/${term}/${slugDate}`,
} as Metadata, } 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}`)); const raw = await readFile(path.join(EXECS_PATH, `${name}${FILETYPE}`));
({ content, data: metadata } = matter(raw)); ({ content, data: metadata } = matter(raw));
const image = await getMemberImagePath(metadata.name); const image = await getMemberImagePath(metadata.name as string);
return { return {
content: convert ? await serialize(content) : content, 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/mdx-js__react": "^1.5.3",
"@types/node": "^16.9.1", "@types/node": "^16.9.1",
"@types/react": "^17.0.14", "@types/react": "^17.0.14",
"@typescript-eslint/eslint-plugin": "4.28.4", "@typescript-eslint/eslint-plugin": "5.22.0",
"@typescript-eslint/parser": "4.28.4", "@typescript-eslint/parser": "5.22.0",
"eslint": "7.32.0", "eslint": "8.14.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.24.0", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "7.24.0", "eslint-plugin-react": "7.29.4",
"eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-react-hooks": "^4.5.0",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"ical-generator": "^3.0.0", "ical-generator": "^3.0.0",
"postcss": "^8.3.0", "postcss": "^8.3.0",
@ -56,6 +56,6 @@
"postcss-flexbugs-fixes": "^5.0.2", "postcss-flexbugs-fixes": "^5.0.2",
"postcss-preset-env": "^7.0.0", "postcss-preset-env": "^7.0.0",
"ts-node": "^10.2.1", "ts-node": "^10.2.1",
"typescript": "4.3.5" "typescript": "4.6.4"
} }
} }

View File

@ -4,34 +4,161 @@ html {
body { body {
/* Default is light theme */ /* Default is light theme */
--primary-background: #ffffff; --light--primary-background: #ffffff;
--secondary-background: #fdf8f5; --light--secondary-background: #fdf8f5;
--primary-accent: #1482e3; --light--primary-accent: #1482e3;
--primary-accent-soft: #5caff9; --light--primary-accent-soft: #5caff9;
--primary-accent-light: #c4e0f8; --light--primary-accent-light: #c4e0f8;
--primary-accent-lighter: #e1eefa; --light--primary-accent-lighter: #e1eefa;
--primary-accent-lightest: #f7fbff; --light--primary-accent-lightest: #f7fbff;
--secondary-accent: #4ed4b2; --light--secondary-accent: #4ed4b2;
--secondary-accent-light: #dcf6f0; --light--secondary-accent-light: #dcf6f0;
--primary-heading: #2a2a62; --light--primary-heading: #2a2a62;
--secondary-heading: #525284;
--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; --light--primary-title: #2a2a62;
--warning-background: #dd0014; --light--primary-subtitle: #2a2a62;
--warning-text: #ffffff; --light--secondary-subtitle: #2a2a62;
--input-background: #f0f0f0; --light--form-invalid: #9f616a;
--input-placeholder-text: #bbbbbb; --light--warning-background: #dd0014;
--input-text: #6b6b6b; --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); background-color: var(--primary-background);
color: var(--text); color: var(--text);
@ -47,10 +174,6 @@ input {
font-family: "Poppins", "sans-serif"; font-family: "Poppins", "sans-serif";
} }
.dark {
/* FIXME: Implement dark theme */
}
h1, h1,
h2, h2,
h3, h3,
@ -117,3 +240,58 @@ h4 + * {
font-weight: 600; 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); height: calc(80rem / 16);
margin-top: auto; margin-top: auto;
padding-left: calc(20rem / 16); padding-left: calc(20rem / 16);
color: var(--primary-title);
} }
.content { .content {

View File

@ -1,6 +1,14 @@
.title {
color: var(--primary-title);
}
.table { .table {
display: table; display: table;
width: 100%; width: 100%;
margin-top: calc(20rem / 16); margin-top: calc(20rem / 16);
margin-bottom: calc(60rem / 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 ( return (
<> <>
<Title>Members</Title> <Title>Members</Title>
<h1>Members</h1> <h1 className={styles.title}>Members</h1>
<p> <p>
{`The members for ${capitalize(props.term)} ${ {`The members for ${capitalize(props.term)} ${
props.year props.year

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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