diff --git a/apps/frontend/src/components/IconButton/IconButton.module.scss b/apps/frontend/src/components/IconButton/IconButton.module.scss
index 1addb2521..46dd9c29c 100644
--- a/apps/frontend/src/components/IconButton/IconButton.module.scss
+++ b/apps/frontend/src/components/IconButton/IconButton.module.scss
@@ -3,24 +3,31 @@
width: 32px;
flex-shrink: 0;
border-radius: 4px;
- display: flex;
- align-items: center;
- justify-content: center;
+ display: inline-grid;
+ place-items: center;
cursor: pointer;
- transition: all 100ms ease-in-out;
user-select: none;
+ transition:
+ border-color 100ms ease-in-out,
+ background-color 100ms ease-in-out,
+ color 100ms ease-in-out;
- &.outline {
+ &[data-disabled] {
+ pointer-events: none;
+ opacity: 0.5;
+ }
+
+ &[data-variant="outline"] {
border: 1px solid var(--border-color);
color: var(--paragraph-color);
-
+
&:hover {
background-color: var(--button-hover-color);
color: var(--heading-color);
}
-
+
&:active {
background-color: var(--button-active-color);
}
}
-}
\ No newline at end of file
+}
diff --git a/apps/frontend/src/components/IconButton/index.tsx b/apps/frontend/src/components/IconButton/index.tsx
index dd7d88310..5cf55a724 100644
--- a/apps/frontend/src/components/IconButton/index.tsx
+++ b/apps/frontend/src/components/IconButton/index.tsx
@@ -1,4 +1,4 @@
-import { ElementType, ReactElement, forwardRef } from "react";
+import { ElementType, ReactNode, forwardRef } from "react";
import classNames from "classnames";
@@ -11,7 +11,7 @@ import styles from "./IconButton.module.scss";
interface Props {
variant?: "outline";
- invert?: boolean;
+ disabled?: boolean;
}
type IconButtonProps
= PolymorphicComponentPropsWithRef<
@@ -24,7 +24,7 @@ const IconButton = forwardRef(
{
children,
className,
- invert,
+ disabled,
as,
variant = "outline",
...props
@@ -35,14 +35,9 @@ const IconButton = forwardRef(
return (
@@ -56,4 +51,4 @@ IconButton.displayName = "IconButton";
export default IconButton as (
props: IconButtonProps
-) => ReactElement | null;
+) => ReactNode | null;
diff --git a/apps/frontend/src/components/Layout/index.tsx b/apps/frontend/src/components/Layout/index.tsx
index 87dc467a1..fa5190027 100644
--- a/apps/frontend/src/components/Layout/index.tsx
+++ b/apps/frontend/src/components/Layout/index.tsx
@@ -14,9 +14,14 @@ import styles from "./Layout.module.scss";
interface LayoutProps {
header?: boolean;
footer?: boolean;
+ feedback?: boolean;
}
-export default function Layout({ header = true, footer = true }: LayoutProps) {
+export default function Layout({
+ header = true,
+ footer = true,
+ feedback,
+}: LayoutProps) {
return (
@@ -32,17 +37,19 @@ export default function Layout({ header = true, footer = true }: LayoutProps) {
{footer &&
}
-
-
-
+ {feedback && (
+
+
+
+ )}
);
}
diff --git a/apps/frontend/src/components/MenuItem/index.tsx b/apps/frontend/src/components/MenuItem/index.tsx
index 779cde714..531e4cfa7 100644
--- a/apps/frontend/src/components/MenuItem/index.tsx
+++ b/apps/frontend/src/components/MenuItem/index.tsx
@@ -1,4 +1,4 @@
-import { ElementType, ReactElement, ReactNode, forwardRef } from "react";
+import { ElementType, ReactNode, forwardRef } from "react";
import classNames from "classnames";
@@ -11,8 +11,7 @@ import styles from "./MenuItem.module.scss";
interface Props {
active?: boolean;
- children: ReactNode;
- className?: string;
+ disabled?: boolean;
}
type MenuItemProps = PolymorphicComponentPropsWithRef<
@@ -21,16 +20,16 @@ type MenuItemProps = PolymorphicComponentPropsWithRef<
>;
const MenuItem = forwardRef(
- (
- { active, children, className, as, ...props }: MenuItemProps,
- ref: PolymorphicRef
+ (
+ { active, children, className, disabled, as, ...props }: MenuItemProps,
+ ref: PolymorphicRef
) => {
- const Component = as ?? "button";
+ const Component = as || "button";
return (
{children}
@@ -49,4 +49,4 @@ MenuItem.displayName = "MenuItem";
export default MenuItem as (
props: MenuItemProps
-) => ReactElement | null;
+) => ReactNode | null;
diff --git a/apps/frontend/src/components/NavigationBar/NavigationBar.module.scss b/apps/frontend/src/components/NavigationBar/NavigationBar.module.scss
index b488d85e7..d42226632 100644
--- a/apps/frontend/src/components/NavigationBar/NavigationBar.module.scss
+++ b/apps/frontend/src/components/NavigationBar/NavigationBar.module.scss
@@ -7,7 +7,12 @@
.brand {
font-size: 24px;
font-weight: 580;
- font-feature-settings: 'cv05' on, 'cv13' on, 'ss07' on, 'cv12' on, 'cv06' on;
+ font-feature-settings:
+ "cv05" on,
+ "cv13" on,
+ "ss07" on,
+ "cv12" on,
+ "cv06" on;
color: var(--blue-500);
cursor: pointer;
transition: all 100ms ease-in-out;
@@ -33,6 +38,10 @@
&.invert {
padding: 12px 12px 0;
+ .icon-button {
+ color: var(--paragraph-color);
+ }
+
.brand {
color: white;
@@ -71,4 +80,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/apps/frontend/src/components/NavigationBar/index.tsx b/apps/frontend/src/components/NavigationBar/index.tsx
index ba5f00f4f..9ca9e63ab 100644
--- a/apps/frontend/src/components/NavigationBar/index.tsx
+++ b/apps/frontend/src/components/NavigationBar/index.tsx
@@ -30,42 +30,41 @@ export default function NavigationBar({ invert }: NavigationBarProps) {
Berkeleytime
{width <= 992 ? (
-
+
) : (
<>
-
-
-
-
-
-
+
+ {({ isActive }) => (
+
+ )}
+
+
+ {({ isActive }) => (
+
+ )}
+
+
+ {({ isActive }) => (
+
+ )}
+
- {account ? (
-
- ) : (
-
- )}
+
>
)}
diff --git a/apps/frontend/src/components/ThemeProvider/ThemeProvider.module.scss b/apps/frontend/src/components/ThemeProvider/ThemeProvider.module.scss
new file mode 100644
index 000000000..0e55d527f
--- /dev/null
+++ b/apps/frontend/src/components/ThemeProvider/ThemeProvider.module.scss
@@ -0,0 +1,363 @@
+:root {
+ --slate-50: #f8fafc;
+ --slate-100: #f1f5f9;
+ --slate-200: #e2e8f0;
+ --slate-300: #cbd5e1;
+ --slate-400: #94a3b8;
+ --slate-500: #64748b;
+ --slate-600: #475569;
+ --slate-700: #334155;
+ --slate-800: #1e293b;
+ --slate-900: #0f172a;
+ --slate-950: #020617;
+
+ --gray-50: #f9fafb;
+ --gray-100: #f3f4f6;
+ --gray-200: #e5e7eb;
+ --gray-300: #d1d5db;
+ --gray-400: #9ca3af;
+ --gray-500: #6b7280;
+ --gray-600: #4b5563;
+ --gray-700: #374151;
+ --gray-800: #1f2937;
+ --gray-900: #111827;
+ --gray-950: #030712;
+
+ --zinc-50: #fafafa;
+ --zinc-100: #f4f4f5;
+ --zinc-200: #e4e4e7;
+ --zinc-300: #d4d4d8;
+ --zinc-400: #a1a1aa;
+ --zinc-500: #71717a;
+ --zinc-600: #52525b;
+ --zinc-700: #3f3f46;
+ --zinc-800: #27272a;
+ --zinc-900: #18181b;
+ --zinc-950: #09090b;
+
+ --neutral-50: #fafafa;
+ --neutral-100: #f5f5f5;
+ --neutral-200: #e5e5e5;
+ --neutral-300: #d4d4d4;
+ --neutral-400: #a3a3a3;
+ --neutral-500: #737373;
+ --neutral-600: #525252;
+ --neutral-700: #404040;
+ --neutral-800: #262626;
+ --neutral-900: #171717;
+ --neutral-950: #0a0a0a;
+
+ --stone-50: #fafaf9;
+ --stone-100: #f5f5f4;
+ --stone-200: #e7e5e4;
+ --stone-300: #d6d3d1;
+ --stone-400: #a8a29e;
+ --stone-500: #78716c;
+ --stone-600: #57534e;
+ --stone-700: #44403c;
+ --stone-800: #292524;
+ --stone-900: #1c1917;
+ --stone-950: #0c0a09;
+
+ --red-50: #fef2f2;
+ --red-100: #fee2e2;
+ --red-200: #fecaca;
+ --red-300: #fca5a5;
+ --red-400: #f87171;
+ --red-500: #ef4444;
+ --red-600: #dc2626;
+ --red-700: #b91c1c;
+ --red-800: #991b1b;
+ --red-900: #7f1d1d;
+ --red-950: #450a0a;
+
+ --orange-50: #fff7ed;
+ --orange-100: #ffedd5;
+ --orange-200: #fed7aa;
+ --orange-300: #fdba74;
+ --orange-400: #fb923c;
+ --orange-500: #f97316;
+ --orange-600: #ea580c;
+ --orange-700: #c2410c;
+ --orange-800: #9a3412;
+ --orange-900: #7c2d12;
+ --orange-950: #431407;
+
+ --amber-50: #fffbeb;
+ --amber-100: #fef3c7;
+ --amber-200: #fde68a;
+ --amber-300: #fcd34d;
+ --amber-400: #fbbf24;
+ --amber-500: #f59e0b;
+ --amber-600: #d97706;
+ --amber-700: #b45309;
+ --amber-800: #92400e;
+ --amber-900: #78350f;
+ --amber-950: #451a03;
+
+ --yellow-50: #fefce8;
+ --yellow-100: #fef9c3;
+ --yellow-200: #fef08a;
+ --yellow-300: #fde047;
+ --yellow-400: #facc15;
+ --yellow-500: #eab308;
+ --yellow-600: #ca8a04;
+ --yellow-700: #a16207;
+ --yellow-800: #854d0e;
+ --yellow-900: #713f12;
+ --yellow-950: #422006;
+
+ --lime-50: #f7fee7;
+ --lime-100: #ecfccb;
+ --lime-200: #d9f99d;
+ --lime-300: #bef264;
+ --lime-400: #a3e635;
+ --lime-500: #84cc16;
+ --lime-600: #65a30d;
+ --lime-700: #4d7c0f;
+ --lime-800: #3f6212;
+ --lime-900: #365314;
+ --lime-950: #1a2e05;
+
+ --green-50: #f0fdf4;
+ --green-100: #dcfce7;
+ --green-200: #bbf7d0;
+ --green-300: #86efac;
+ --green-400: #4ade80;
+ --green-500: #22c55e;
+ --green-600: #16a34a;
+ --green-700: #15803d;
+ --green-800: #166534;
+ --green-900: #14532d;
+ --green-950: #052e16;
+
+ --emerald-50: #ecfdf5;
+ --emerald-100: #d1fae5;
+ --emerald-200: #a7f3d0;
+ --emerald-300: #6ee7b7;
+ --emerald-400: #34d399;
+ --emerald-500: #10b981;
+ --emerald-600: #059669;
+ --emerald-700: #047857;
+ --emerald-800: #065f46;
+ --emerald-900: #064e3b;
+ --emerald-950: #022c22;
+
+ --teal-50: #f0fdfa;
+ --teal-100: #ccfbf1;
+ --teal-200: #99f6e4;
+ --teal-300: #5eead4;
+ --teal-400: #2dd4bf;
+ --teal-500: #14b8a6;
+ --teal-600: #0d9488;
+ --teal-700: #0f766e;
+ --teal-800: #115e59;
+ --teal-900: #134e4a;
+ --teal-950: #042f2e;
+
+ --cyan-50: #ecfeff;
+ --cyan-100: #cffafe;
+ --cyan-200: #a5f3fc;
+ --cyan-300: #67e8f9;
+ --cyan-400: #22d3ee;
+ --cyan-500: #06b6d4;
+ --cyan-600: #0891b2;
+ --cyan-700: #0e7490;
+ --cyan-800: #155e75;
+ --cyan-900: #164e63;
+ --cyan-950: #083344;
+
+ --sky-50: #f0f9ff;
+ --sky-100: #e0f2fe;
+ --sky-200: #bae6fd;
+ --sky-300: #7dd3fc;
+ --sky-400: #38bdf8;
+ --sky-500: #0ea5e9;
+ --sky-600: #0284c7;
+ --sky-700: #0369a1;
+ --sky-800: #075985;
+ --sky-900: #0c4a6e;
+ --sky-950: #083344;
+
+ --blue-50: #eff6ff;
+ --blue-100: #dbeafe;
+ --blue-200: #bfdbfe;
+ --blue-300: #93c5fd;
+ --blue-400: #60a5fa;
+ --blue-500: #3b82f6;
+ --blue-600: #2563eb;
+ --blue-700: #1d4ed8;
+ --blue-800: #1e40af;
+ --blue-900: #1e3a8a;
+ --blue-950: #172554;
+
+ --indigo-50: #eef2ff;
+ --indigo-100: #e0e7ff;
+ --indigo-200: #c7d2fe;
+ --indigo-300: #a5b4fc;
+ --indigo-400: #818cf8;
+ --indigo-500: #6366f1;
+ --indigo-600: #4f46e5;
+ --indigo-700: #4338ca;
+ --indigo-800: #3730a3;
+ --indigo-900: #312e81;
+ --indigo-950: #1e1b4b;
+
+ --violet-50: #f5f3ff;
+ --violet-100: #ede9fe;
+ --violet-200: #ddd6fe;
+ --violet-300: #c4b5fd;
+ --violet-400: #a78bfa;
+ --violet-500: #8b5cf6;
+ --violet-600: #7c3aed;
+ --violet-700: #6d28d9;
+ --violet-800: #5b21b6;
+ --violet-900: #4c1d95;
+ --violet-950: #2e1065;
+
+ --purple-50: #faf5ff;
+ --purple-100: #f3e8ff;
+ --purple-200: #e9d5ff;
+ --purple-300: #d8b4fe;
+ --purple-400: #c084fc;
+ --purple-500: #a855f7;
+ --purple-600: #9333ea;
+ --purple-700: #7e22ce;
+ --purple-800: #6b21a8;
+ --purple-900: #581c87;
+ --purple-950: #3b0764;
+
+ --fuchsia-50: #fdf4ff;
+ --fuchsia-100: #fae8ff;
+ --fuchsia-200: #f5d0fe;
+ --fuchsia-300: #f0abfc;
+ --fuchsia-400: #e879f9;
+ --fuchsia-500: #d946ef;
+ --fuchsia-600: #c026d3;
+ --fuchsia-700: #a21caf;
+ --fuchsia-800: #86198f;
+ --fuchsia-900: #701a75;
+ --fuchsia-950: #4a044e;
+
+ --pink-50: #fdf2f8;
+ --pink-100: #fce7f3;
+ --pink-200: #fbcfe8;
+ --pink-300: #f9a8d4;
+ --pink-400: #f472b6;
+ --pink-500: #ec4899;
+ --pink-600: #db2777;
+ --pink-700: #be185d;
+ --pink-800: #9d174d;
+ --pink-900: #831843;
+ --pink-950: #500724;
+
+ --rose-50: #fff1f2;
+ --rose-100: #ffe4e6;
+ --rose-200: #fecdd3;
+ --rose-300: #fda4af;
+ --rose-400: #fb7185;
+ --rose-500: #f43f5e;
+ --rose-600: #e11d48;
+ --rose-700: #be123c;
+ --rose-800: #9f1239;
+ --rose-900: #881337;
+ --rose-950: #4c0519;
+
+ --light-foreground-color: white;
+ --light-background-color: var(--slate-50);
+ --light-backdrop-color: var(--slate-100);
+
+ --light-border-color: rgb(0 0 0 / 10%);
+
+ --light-heading-color: var(--slate-900);
+ --light-paragraph-color: var(--slate-500);
+ --light-label-color: var(--slate-400);
+
+ // --light-button-color: white;
+ --light-button-hover-color: var(--slate-100);
+ --light-button-active-color: var(--slate-200);
+
+ --light-tooltip-color: var(--neutral-900);
+
+ --dark-foreground-color: var(--zinc-800);
+ --dark-background-color: var(--zinc-900);
+ --dark-backdrop-color: var(--zinc-950);
+
+ --dark-border-color: var(--zinc-700);
+
+ --dark-heading-color: white;
+ --dark-paragraph-color: var(--zinc-400);
+ --dark-label-color: var(--zinc-500);
+
+ // --dark-button-color: var(--zinc-800);
+ --dark-button-hover-color: var(--zinc-700);
+ --dark-button-active-color: var(--zinc-600);
+
+ --dark-tooltip-color: var(--zinc-950);
+}
+
+@mixin light-theme {
+ --foreground-color: var(--light-foreground-color);
+ --background-color: var(--light-background-color);
+ --backdrop-color: var(--light-backdrop-color);
+
+ --border-color: var(--light-border-color);
+
+ --heading-color: var(--light-heading-color);
+ --paragraph-color: var(--light-paragraph-color);
+ --label-color: var(--light-label-color);
+
+ // --button-color: var(--light-button-color);
+ --button-hover-color: var(--light-button-hover-color);
+ --button-active-color: var(--light-button-active-color);
+
+ --tooltip-color: var(--light-tooltip-color);
+}
+
+@mixin dark-theme {
+ --foreground-color: var(--dark-foreground-color);
+ --background-color: var(--dark-background-color);
+ --backdrop-color: var(--dark-backdrop-color);
+
+ --border-color: var(--dark-border-color);
+
+ --heading-color: var(--dark-heading-color);
+ --paragraph-color: var(--dark-paragraph-color);
+ --label-color: var(--dark-label-color);
+
+ // --button-color: var(--dark-button-color);
+ --button-hover-color: var(--dark-button-hover-color);
+ --button-active-color: var(--dark-button-active-color);
+
+ --tooltip-color: var(--dark-tooltip-color);
+}
+
+.root {
+ display: flex;
+ flex-direction: column;
+}
+
+body[data-theme="dark"] {
+ @include dark-theme;
+}
+
+body[data-theme="light"] {
+ @include light-theme;
+}
+
+body:not([data-theme]) {
+ @include light-theme;
+
+ @media (prefers-color-scheme: dark) {
+ @include dark-theme;
+ }
+}
+
+body {
+ background-color: var(--background-color);
+ font-family: Inter, sans-serif;
+
+ @supports (font-variation-settings: normal) {
+ font-family: InterVariable, sans-serif;
+ }
+}
diff --git a/apps/frontend/src/components/ThemeProvider/index.tsx b/apps/frontend/src/components/ThemeProvider/index.tsx
new file mode 100644
index 000000000..8b74d61ae
--- /dev/null
+++ b/apps/frontend/src/components/ThemeProvider/index.tsx
@@ -0,0 +1,48 @@
+import { ReactNode, useEffect, useState } from "react";
+
+import { TooltipProvider } from "@radix-ui/react-tooltip";
+import { IconoirProvider } from "iconoir-react";
+
+import ThemeContext, { Theme } from "@/context/ThemeContext";
+
+import styles from "./ThemeProvider.module.scss";
+
+interface ThemeProviderProps {
+ children: ReactNode;
+}
+
+export default function ThemeProvider({ children }: ThemeProviderProps) {
+ const [theme, setTheme] = useState