Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customizable theme colors #129

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@headlessui/react": "^1.7.4",
"@headlessui/react": "^1.7.5",
"firebase": "^9.14.0",
"he": "^1.2.0",
"luxon": "^3.1.0",
Expand Down
2 changes: 1 addition & 1 deletion client/public/service-worker.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This dummy service worker is to hopefully migrate all users stuck on the old service worker URL
// (`/service-worker.js`) to the new URL (`/sw.js`). The old service worker cannot detect the new service worker
// because the URL has changed, so it will stop giving updated app versions and fall behind. This dummy worker
// attempts to remedy that by sending an update to the old service worker telling it to self destruct, which will
// attempts to remedy that by sending an update to the old service worker telling it to self-destruct, which will
// hopefully cause the new worker to install itself.

// Not sure if this is needed because the old service worker logic should already call `skipWaiting()` for us, but
Expand Down
2 changes: 2 additions & 0 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import PageNotFound from './pages/404';
import SgyAuthRedirect from './pages/SgyAuthRedirect';

// Components
import ThemeHandler from './components/layout/ThemeHandler';
import FaviconHandler from './components/schedule/FaviconHandler';
import InstallModal from './components/layout/InstallModal';
import SgyInitResults from './components/firebase/SgyInitResults';
Expand Down Expand Up @@ -123,6 +124,7 @@ export default function App() {
<TimeProvider value={date}>
<PageVisibility onChange={() => navigator.serviceWorker.getRegistration().then(res => res?.update())}/>
<FaviconHandler />
<ThemeHandler />
{signInCheckResult?.signedIn && <FirebaseUserDataUpdater />}

<Routes>
Expand Down
10 changes: 5 additions & 5 deletions client/src/components/firebase/SgyInitResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Dialog} from '@headlessui/react';

// Components
import CenteredModal from '../layout/CenteredModal';
import OutlineButton, {DangerOutlineButton, SuccessOutlineButton} from '../layout/OutlineButton';
import OutlineButton, {ThemeOutlineButton, SuccessOutlineButton} from '../layout/OutlineButton';
import Loading from '../layout/Loading';

// Auth
Expand Down Expand Up @@ -98,16 +98,16 @@ export default function SgyInitResults() {

<section className="flex gap-3">
{results && (confirmDisable ? (<>
<DangerOutlineButton onClick={disableSchoology}>
<ThemeOutlineButton onClick={disableSchoology}>
Yes, Disable Schoology
</DangerOutlineButton>
</ThemeOutlineButton>
<OutlineButton onClick={() => setConfirm(false)}>
Take Me Back!
</OutlineButton>
</>) : (<>
<DangerOutlineButton onClick={() => setConfirm(true)}>
<ThemeOutlineButton onClick={() => setConfirm(true)}>
Disable Schoology
</DangerOutlineButton>
</ThemeOutlineButton>
<SuccessOutlineButton onClick={enableSchoology}>
Looks Good!
</SuccessOutlineButton>
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/layout/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {ReactNode} from 'react';
type BadgeProps = {children: ReactNode};
export default function Badge(props: BadgeProps) {
return (
<span className="rounded-full text-white font-bold bg-theme shadow shadow-red-700/40 text-sm h-max px-1.5 pb-0.5 ml-2.5">
<span className="rounded-full text-white font-bold bg-theme shadow shadow-theme-tertiary/40 text-sm h-max px-1.5 pb-0.5 ml-2.5">
{props.children}
</span>
)
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/layout/HeaderPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function HeaderPage(props: HeaderPageProps) {

export function Header(props: {children: ReactNode}) {
return (
<header className="px-5 py-3 flex items-center gap-4 text-white bg-gradient-to-r from-theme to-red-700 dark:to-[#eb144c] rounded-lg shadow-lg shadow-red-700/40">
<header className="px-5 py-3 flex items-center gap-4 text-white bg-gradient-to-r from-theme to-theme-secondary rounded-lg shadow-lg shadow-theme-tertiary/40">
{props.children}
</header>
)
Expand Down
12 changes: 10 additions & 2 deletions client/src/components/layout/Logo.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import {useContext} from 'react';
import UserDataContext from '../../contexts/UserDataContext';


export default function Logo(props: {className?: string}) {
const foregroundColor = '#a51618';
const backgroundColor = '#7f1618';
const userData = useContext(UserDataContext);

// TODO: see todo in `Wave.tsx`
const colors = userData.options.theme === 'dark' ? userData.colors.dark : userData.colors.light;
const foregroundColor = userData.colors.id !== 'default' ? colors.theme : '#a51618';
const backgroundColor = userData.colors.id !== 'default' ? colors.accent : '#7f1618';

return (
<svg
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/layout/OutlineButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ export default function OutlineButton(props: OutlineButtonProps) {
)
}

export function DangerOutlineButton(props: OutlineButtonProps) {
export function ThemeOutlineButton(props: OutlineButtonProps) {
const {children, ...buttonProps} = props;

return (
<button className="text-theme border border-theme hover:text-white hover:bg-gradient-to-br hover:from-theme hover:to-red-700 dark:hover:from-red-500 dark:hover:to-[#eb144c] hover:shadow-lg hover:shadow-red-700/40 hover:border-transparent px-3 py-2 rounded transition-shadow duration-100 focus:outline-none focus-visible:ring-[3px] focus-visible:ring-theme/25" {...buttonProps}>
<button className="text-theme border border-theme hover:text-white hover:bg-gradient-to-br hover:from-theme hover:to-theme-secondary hover:shadow-lg hover:shadow-theme-tertiary/40 hover:border-transparent px-3 py-2 rounded transition-shadow duration-100 focus:outline-none focus-visible:ring-[3px] focus-visible:ring-theme/25" {...buttonProps}>
{children}
</button>
)
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {useEffect, useState} from 'react';
import {Link} from 'react-router-dom';
import {useScreenType} from '../../hooks/useScreenType';
import Logo from './Logo';

// Authentication
import {useSigninCheck} from 'reactfire';

// Components
import Logo from './Logo';
import SidebarItem from './SidebarItem';
import GoogleSignInBtn from '../firebase/GoogleSignInBtn';
import GoogleSignOutBtn from '../firebase/GoogleSignOutBtn';
Expand Down
69 changes: 69 additions & 0 deletions client/src/components/layout/ThemeHandler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {useContext} from 'react';
import UserDataContext from '../../contexts/UserDataContext';
import {hexToRgb} from '../../util/progressBarColor';


type Colors = { theme: string, accent: string, shadow: string };
export type ColorTheme = { id: 'default' | 'goldenrod' | 'electric-violet', dark: Colors, light: Colors }

export default function ThemeHandler() {
const {colors} = useContext(UserDataContext);

return (
<style>
{`:root {
--theme: ${hexToRgb(colors.light.theme)?.join(' ')};
--theme-secondary: ${hexToRgb(colors.light.accent)?.join(' ')};
--theme-tertiary: ${hexToRgb(colors.light.shadow)?.join(' ')};
}
:root.dark {
--theme: ${hexToRgb(colors.dark.theme)?.join(' ')};
--theme-secondary: ${hexToRgb(colors.dark.accent)?.join(' ')};
--theme-tertiary: ${hexToRgb(colors.dark.shadow)?.join(' ')};
}`}
</style>
)
}

export const defaultTheme: ColorTheme = {
id: 'default',
dark: {
theme: '#ff594c',
accent: '#eb144c',
shadow: '#b91c1c'
},
light: {
theme: '#a51618',
accent: '#b91c1c',
shadow: '#b91c1c'
}
}

export const goldenrod: ColorTheme = {
id: 'goldenrod',
dark: {
theme: '#f59e0b',
accent: '#ea580c',
shadow: '#c2410c'
},
light: {
theme: '#f59e0b',
accent: '#f97316',
shadow: '#c2410c'
}
}

export const electricViolet: ColorTheme = {
id: 'electric-violet',
dark: {
theme: '#e879f9',
accent: '#9024f5',
shadow: '#6b21a8'
},
// TODO: play around with these and make better
light: {
theme: '#913399',
accent: '#5d30a6',
shadow: '#7e22ce'
}
}
24 changes: 16 additions & 8 deletions client/src/components/layout/Wave.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import {useContext} from 'react';
import UserDataContext from '../../contexts/UserDataContext';


export default function Wave() {
const leftColor = 'ff594c';
const rightColor = 'eb144c';
const userData = useContext(UserDataContext);

// TODO: make cleaner, extract logic?
const colors = userData.options.theme === 'dark' ? userData.colors.dark : userData.colors.light;
const leftColor = userData.colors.id !== 'default' ? colors.theme : '#ff594c';
const rightColor = userData.colors.id !== 'default' ? colors.accent : '#eb144c';

return (
// Constrain the width between 800px and 100vw as a hack for phone aspect ratio.
Expand All @@ -9,8 +17,8 @@ export default function Wave() {
<svg className="fixed top-0 left-0 -z-10 w-[max(800px,_100vw)]" viewBox="0 0 1440 700" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="low">
<stop offset="5%" stopColor={`#${rightColor}44`}/>
<stop offset="95%" stopColor={`#${leftColor}44`}/>
<stop offset="5%" stopColor={`${rightColor}44`}/>
<stop offset="95%" stopColor={`${leftColor}44`}/>
</linearGradient>
</defs>
<path
Expand All @@ -20,8 +28,8 @@ export default function Wave() {
/>
<defs>
<linearGradient id="middle">
<stop offset="5%" stopColor={`#${rightColor}66`}/>
<stop offset="95%" stopColor={`#${leftColor}66`}/>
<stop offset="5%" stopColor={`${rightColor}66`}/>
<stop offset="95%" stopColor={`${leftColor}66`}/>
</linearGradient>
</defs>
<path
Expand All @@ -31,8 +39,8 @@ export default function Wave() {
/>
<defs>
<linearGradient id="high">
<stop offset="5%" stopColor={`#${rightColor}88`}/>
<stop offset="95%" stopColor={`#${leftColor}88`}/>
<stop offset="5%" stopColor={`${rightColor}88`}/>
<stop offset="95%" stopColor={`${leftColor}88`}/>
</linearGradient>
</defs>
<path
Expand Down
6 changes: 3 additions & 3 deletions client/src/components/lists/ClubComponentModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Club} from '@watt/shared/data/clubs';

// Components
import CenteredModal from '../layout/CenteredModal';
import OutlineButton, {DangerOutlineButton} from '../layout/OutlineButton';
import OutlineButton, {ThemeOutlineButton} from '../layout/OutlineButton';
import Badge from '../layout/Badge';

// Context
Expand Down Expand Up @@ -82,9 +82,9 @@ export default function ClubComponentModal(props: ClubComponentModalProps) {
<a href={prefilledLink} tabIndex={-1} target="_blank" rel="noopener noreferrer">
<OutlineButton>Check In</OutlineButton>
</a>
<DangerOutlineButton onClick={() => setIsOpen(false)}>
<ThemeOutlineButton onClick={() => setIsOpen(false)}>
Close
</DangerOutlineButton>
</ThemeOutlineButton>
</section>
</CenteredModal>
)
Expand Down
6 changes: 3 additions & 3 deletions client/src/components/lists/CourseComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Course} from '@watt/shared/data/courses';

// Components
import CenteredModal from '../layout/CenteredModal';
import {DangerOutlineButton} from '../layout/OutlineButton';
import {ThemeOutlineButton} from '../layout/OutlineButton';


export default function CourseComponent(props: Course) {
Expand Down Expand Up @@ -58,9 +58,9 @@ export default function CourseComponent(props: Course) {
</section>

<section className="flex gap-3 flex-wrap justify-end">
<DangerOutlineButton onClick={() => setModal(false)}>
<ThemeOutlineButton onClick={() => setModal(false)}>
Close
</DangerOutlineButton>
</ThemeOutlineButton>
</section>
</CenteredModal>
</>
Expand Down
6 changes: 3 additions & 3 deletions client/src/components/lists/StaffComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Staff} from '@watt/shared/data/staff';

// Components
import CenteredModal from '../layout/CenteredModal';
import OutlineButton, {DangerOutlineButton} from '../layout/OutlineButton';
import OutlineButton, {ThemeOutlineButton} from '../layout/OutlineButton';
import PillClubComponent from './PillClubComponent';

// Context
Expand Down Expand Up @@ -87,9 +87,9 @@ export default function StaffComponent(props: Staff & {id: string}) {
Add to my list
</OutlineButton>
)}
<DangerOutlineButton onClick={() => setModal(false)}>
<ThemeOutlineButton onClick={() => setModal(false)}>
Close
</DangerOutlineButton>
</ThemeOutlineButton>
</section>
</CenteredModal>
</>
Expand Down
8 changes: 7 additions & 1 deletion client/src/contexts/UserDataContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {createContext} from 'react';
import {AllSgyPeriod} from './SgyDataContext';
import {ColorTheme, defaultTheme} from '../components/layout/ThemeHandler';


// Represents a course on Schoology.
Expand Down Expand Up @@ -27,7 +28,11 @@ export type UserData = {
5: SgyPeriodData, 6: SgyPeriodData, 7: SgyPeriodData, 8: SgyPeriodData,
P: SgyPeriodData, S: SgyPeriodData, H: SgyPeriodData
},
options: {theme: string, time: string, period0: boolean, period8: boolean, clock: boolean, sgy: boolean},
options: {
theme: string, time: string, period0: boolean, period8: boolean,
clock: boolean, sgy: boolean
},
colors: ColorTheme
id: string,
gradYear: number, // The year (eg. 2023) or `0` if unset
sgy: {
Expand Down Expand Up @@ -68,6 +73,7 @@ export const defaultUserData: UserData = {
clock: true,
sgy: false
},
colors: defaultTheme,
id: '00000',
gradYear: 0,
sgy: {
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/Testing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {ReactNode, useContext, useEffect, useState} from 'react';

// Components
import CenteredMessage from '../components/layout/CenteredMessage';
import OutlineButton, {DangerOutlineButton, SuccessOutlineButton} from '../components/layout/OutlineButton';
import OutlineButton, {ThemeOutlineButton, SuccessOutlineButton} from '../components/layout/OutlineButton';
import Period from '../components/schedule/Period';
import Loading from '../components/layout/Loading';
import WIP from '../components/layout/WIP';
Expand Down Expand Up @@ -127,7 +127,7 @@ export default function Testing() {

<ContentBox className="flex gap-2 justify-center">
<OutlineButton>Add to my list</OutlineButton>
<DangerOutlineButton>Disable Schoology</DangerOutlineButton>
<ThemeOutlineButton>Disable Schoology</ThemeOutlineButton>
<SuccessOutlineButton>Looks good!</SuccessOutlineButton>
</ContentBox>

Expand Down
Loading