diff --git a/web/index.html b/web/index.html index 5ac2c90..9ac055a 100644 --- a/web/index.html +++ b/web/index.html @@ -4,7 +4,14 @@ - Maps Service + StrafesNET | Maps + + + +
diff --git a/web/src/App.tsx b/web/src/App.tsx index 26c9037..9537789 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,5 +1,5 @@ import { Routes, Route } from 'react-router-dom' -import { ThemeProvider } from '@mui/material' +import { ThemeProvider, CssBaseline } from '@mui/material' import { theme } from '@/app/lib/theme' // Pages @@ -22,6 +22,7 @@ import NotFound from '@/app/not-found/page' function App() { return ( + } /> } /> diff --git a/web/src/app/_components/AnimatedBackground.tsx b/web/src/app/_components/AnimatedBackground.tsx new file mode 100644 index 0000000..4649cf7 --- /dev/null +++ b/web/src/app/_components/AnimatedBackground.tsx @@ -0,0 +1,34 @@ +import { Box } from '@mui/material'; +import { surface } from '@/app/lib/colors'; + +const AnimatedBackground: React.FC = () => { + return ( + + + + ); +}; + +export default AnimatedBackground; diff --git a/web/src/app/_components/carousel.tsx b/web/src/app/_components/carousel.tsx index 64ee693..67862bb 100644 --- a/web/src/app/_components/carousel.tsx +++ b/web/src/app/_components/carousel.tsx @@ -75,7 +75,7 @@ export function Carousel({ title, items, renderItem, vie sx={{ color: 'primary.main', '&:hover': { - backgroundColor: 'rgba(99, 102, 241, 0.1)', + backgroundColor: 'rgba(167, 139, 250, 0.08)', }, }} > @@ -93,12 +93,12 @@ export function Carousel({ title, items, renderItem, vie transform: 'translateY(-50%)', zIndex: 2, backgroundColor: 'background.paper', - border: '1px solid rgba(99, 102, 241, 0.2)', + border: '1px solid rgba(167, 139, 250, 0.15)', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)', '&:hover': { backgroundColor: 'background.paper', - borderColor: 'rgba(99, 102, 241, 0.4)', - boxShadow: '0 8px 20px rgba(99, 102, 241, 0.3)', + borderColor: 'rgba(167, 139, 250, 0.3)', + boxShadow: '0 8px 20px rgba(167, 139, 250, 0.2)', }, visibility: scrollPosition <= 5 ? 'hidden' : 'visible', }} @@ -146,12 +146,12 @@ export function Carousel({ title, items, renderItem, vie transform: 'translateY(-50%)', zIndex: 2, backgroundColor: 'background.paper', - border: '1px solid rgba(99, 102, 241, 0.2)', + border: '1px solid rgba(167, 139, 250, 0.15)', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)', '&:hover': { backgroundColor: 'background.paper', - borderColor: 'rgba(99, 102, 241, 0.4)', - boxShadow: '0 8px 20px rgba(99, 102, 241, 0.3)', + borderColor: 'rgba(167, 139, 250, 0.3)', + boxShadow: '0 8px 20px rgba(167, 139, 250, 0.2)', }, visibility: scrollPosition >= maxScroll - 5 ? 'hidden' : 'visible', }} diff --git a/web/src/app/_components/comments/CommentsAndAuditSection.tsx b/web/src/app/_components/comments/CommentsAndAuditSection.tsx index 009911c..2d9a332 100644 --- a/web/src/app/_components/comments/CommentsAndAuditSection.tsx +++ b/web/src/app/_components/comments/CommentsAndAuditSection.tsx @@ -6,6 +6,7 @@ import { Tab, keyframes } from "@mui/material"; +import { semantic } from "@/app/lib/colors"; import CommentsTabPanel from './CommentsTabPanel'; import AuditEventsTabPanel from './AuditEventsTabPanel'; import { AuditEvent, AuditEventType } from "@/app/ts/AuditEvent"; @@ -75,7 +76,7 @@ export default function CommentsAndAuditSection({ width: 8, height: 8, borderRadius: '50%', - backgroundColor: '#ff9800', + backgroundColor: semantic.warning, animation: `${pulse} 2s ease-in-out infinite` }} /> diff --git a/web/src/app/_components/header.tsx b/web/src/app/_components/header.tsx index e739a0b..3b601d9 100644 --- a/web/src/app/_components/header.tsx +++ b/web/src/app/_components/header.tsx @@ -1,6 +1,7 @@ import { Link } from "react-router-dom" -import { useState, useRef } from "react"; +import { useState } from "react"; import { useUser } from "@/app/hooks/useUser"; +import { primary, text, border, fill } from "@/app/lib/colors"; import AppBar from "@mui/material/AppBar"; import Toolbar from "@mui/material/Toolbar"; @@ -10,51 +11,44 @@ import Box from "@mui/material/Box"; import Menu from "@mui/material/Menu"; import MenuItem from "@mui/material/MenuItem"; import IconButton from "@mui/material/IconButton"; +import Avatar from "@mui/material/Avatar"; import MenuIcon from "@mui/icons-material/Menu"; import Drawer from "@mui/material/Drawer"; import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; import ListItemButton from "@mui/material/ListItemButton"; import ListItemText from "@mui/material/ListItemText"; +import LoginIcon from "@mui/icons-material/Login"; import useMediaQuery from "@mui/material/useMediaQuery"; import { useTheme } from "@mui/material/styles"; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; -interface HeaderButton { - name: string; - href: string; -} - -const navItems: HeaderButton[] = [ +const navItems = [ { name: "Home", href: "/" }, { name: "Submissions", href: "/submissions" }, { name: "Mapfixes", href: "/mapfixes" }, { name: "Maps", href: "/maps" }, ]; -function HeaderButton(header: HeaderButton) { - return ( - - ); -} +const quickLinks = [ + { name: "Bhop", href: "https://www.roblox.com/games/5315046213" }, + { name: "Bhop Maptest", href: "https://www.roblox.com/games/517201717" }, + { name: "Surf", href: "https://www.roblox.com/games/5315066937" }, + { name: "Surf Maptest", href: "https://www.roblox.com/games/517206177" }, + { name: "Fly Trials", href: "https://www.roblox.com/games/12591611759" }, + { name: "Fly Trials Maptest", href: "https://www.roblox.com/games/12724901535" }, +]; export default function Header() { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down('md')); const [mobileOpen, setMobileOpen] = useState(false); - const hasAnimated = useRef(false); const getAuthUrl = () => { const hostname = window.location.hostname; - - // Production only if (hostname === 'maps.strafes.net') { return 'https://auth.strafes.net'; } - - // Default to staging (works for staging.strafes.net and localhost) return 'https://auth.staging.strafes.net'; }; @@ -86,9 +80,20 @@ export default function Header() { setQuickLinksAnchor(null); }; - // Mobile navigation drawer content const drawer = ( - + + + StrafesNET + {navItems.map((item) => ( @@ -100,7 +105,7 @@ export default function Header() { {isLoggedIn && user && ( - + )} @@ -122,125 +127,88 @@ export default function Header() { ); - const quickLinks = [ - { name: "Bhop", href: "https://www.roblox.com/games/5315046213" }, - { name: "Bhop Maptest", href: "https://www.roblox.com/games/517201717" }, - { name: "Surf", href: "https://www.roblox.com/games/5315066937" }, - { name: "Surf Maptest", href: "https://www.roblox.com/games/517206177" }, - { name: "Fly Trials", href: "https://www.roblox.com/games/12591611759" }, - { name: "Fly Trials Maptest", href: "https://www.roblox.com/games/12724901535" }, - ]; - return ( - - + + {isMobile && ( )} - {/* Desktop navigation */} + {/* Brand */} {!isMobile && ( - - {/* Logo/Brand */} - + - - { - hasAnimated.current = true; - }} - > - StrafesNET - - + StrafesNET + + + Maps + + + )} + + {isMobile && ( + + + StrafesNET + + + Maps + + + )} + + {/* Desktop nav items */} + {!isMobile && ( + <> {navItems.map((item) => ( ))} - - {/* Quick Links Dropdown */} - - - - {quickLinks.map(link => ( - - {link.name} - - ))} - - - + )} - {/* Spacer for mobile view */} - {isMobile && } + - {/* Right side of nav */} - - {!isMobile && isLoggedIn && user && ( - - )} - {!isMobile && isLoggedIn && user ? ( - - + + {quickLinks.map(link => ( + - {user.Username} - - {user.Username} - - - - - Manage Account - - - - ) : !isMobile && ( - - )} + {link.name} + + ))} + + + )} - {/* In mobile view, display just the avatar if logged in */} - {isMobile && isLoggedIn && user && ( + {/* Submit + Auth */} + {!isMobile && isLoggedIn && user && ( + + )} + + {isLoggedIn && user ? ( + - {user.Username} + + {user.Username?.slice(0, 2).toUpperCase()} + - )} - + + + Manage Account + + + + ) : ( + + )} {/* Mobile drawer */} @@ -441,9 +374,7 @@ export default function Header() { variant="temporary" open={mobileOpen} onClose={handleDrawerToggle} - ModalProps={{ - keepMounted: true, - }} + ModalProps={{ keepMounted: true }} sx={{ '& .MuiDrawer-paper': { boxSizing: 'border-box', @@ -455,4 +386,4 @@ export default function Header() { ); -} \ No newline at end of file +} diff --git a/web/src/app/_components/mapCard.tsx b/web/src/app/_components/mapCard.tsx index 79ee73a..397ac49 100644 --- a/web/src/app/_components/mapCard.tsx +++ b/web/src/app/_components/mapCard.tsx @@ -5,6 +5,7 @@ import {Link} from "react-router-dom"; import {useAssetThumbnail, useUserThumbnail} from "@/app/hooks/useThumbnails"; import {useUsername} from "@/app/hooks/useUsername"; import { getGameName } from "@/app/utils/games"; +import { primary, gameColors } from "@/app/lib/colors"; interface MapCardProps { displayName: string; @@ -119,13 +120,13 @@ export function MapCard(props: MapCardProps) { flexWrap: 'wrap', }}> - + {getGameName(props.gameID)} - + {props.type === 'mapfix' && usernameLoading ? ( ) : ( diff --git a/web/src/app/_components/review/WorkflowStepper.tsx b/web/src/app/_components/review/WorkflowStepper.tsx index a1d442d..efcdf2e 100644 --- a/web/src/app/_components/review/WorkflowStepper.tsx +++ b/web/src/app/_components/review/WorkflowStepper.tsx @@ -6,6 +6,7 @@ import PendingIcon from '@mui/icons-material/Pending'; import WarningIcon from '@mui/icons-material/Warning'; import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; import { Status } from '@/app/ts/Status'; +import { semantic } from "@/app/lib/colors"; const pulse = keyframes` 0%, 100% { @@ -188,8 +189,8 @@ const WorkflowStepper: React.FC = ({ currentStatus, type, icon: InfoOutlinedIcon, title: 'Not Yet Submitted', message: 'Your submission has been created but has not been submitted. Click "Submit" to submit it.', - color: '#2196f3', - bgColor: 'rgba(33, 150, 243, 0.08)' + color: semantic.info, + bgColor: 'rgba(56, 189, 248, 0.08)' }; } if (isChangesRequested) { @@ -197,8 +198,8 @@ const WorkflowStepper: React.FC = ({ currentStatus, type, icon: WarningIcon, title: 'Changes Requested', message: 'Review comments and audit events, make modifications, and submit again.', - color: '#ff9800', - bgColor: 'rgba(255, 152, 0, 0.08)' + color: semantic.warning, + bgColor: 'rgba(251, 191, 36, 0.08)' }; } return null; diff --git a/web/src/app/_components/statusChip.tsx b/web/src/app/_components/statusChip.tsx index f374f28..6e09eeb 100644 --- a/web/src/app/_components/statusChip.tsx +++ b/web/src/app/_components/statusChip.tsx @@ -1,86 +1,76 @@ import {JSX} from "react"; import {Cancel, CheckCircle, Pending} from "@mui/icons-material"; import {Chip} from "@mui/material"; +import { semantic, text } from "@/app/lib/colors"; -export const StatusChip = ({status}: { status: number }): JSX.Element => { - let color: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' = 'default'; - let icon: JSX.Element = ; - let label: string = 'Unknown'; +interface StatusConfig { + bg: string; + color: string; + border: string; + icon: JSX.Element; + label: string; +} + +function getStatusConfig(status: number): StatusConfig { + const warn = { + bg: `rgba(251, 191, 36, 0.08)`, + color: semantic.warning, + border: `rgba(251, 191, 36, 0.2)`, + }; + const info = { + bg: `rgba(56, 189, 248, 0.08)`, + color: semantic.info, + border: `rgba(56, 189, 248, 0.2)`, + }; + const success = { + bg: `rgba(74, 222, 128, 0.08)`, + color: semantic.success, + border: `rgba(74, 222, 128, 0.2)`, + }; + const error = { + bg: `rgba(248, 113, 113, 0.08)`, + color: semantic.error, + border: `rgba(248, 113, 113, 0.2)`, + }; + const gray = { + bg: `rgba(161, 161, 170, 0.08)`, + color: text.tertiary, + border: `rgba(161, 161, 170, 0.2)`, + }; switch (status) { - case 0: - color = 'warning'; - icon = ; - label = 'Under Construction'; - break; - case 1: - color = 'warning'; - icon = ; - label = 'Changes Requested'; - break; - case 2: - color = 'info'; - icon = ; - label = 'Submitting'; - break; - case 3: - color = 'warning'; - icon = ; - label = 'Under Review'; - break; - case 4: - color = 'warning'; - icon = ; - label = 'Script Review'; - break; - case 5: - color = 'info'; - icon = ; - label = 'Validating'; - break; - case 6: - color = 'success'; - icon = ; - label = 'Validated'; - break; - case 7: - color = 'info'; - icon = ; - label = 'Uploading'; - break; - case 8: - color = 'success'; - icon = ; - label = 'Uploaded'; - break; - case 11: - color = 'info'; - icon = ; - label = 'Releasing'; - break; - case 9: - color = 'error'; - icon = ; - label = 'Rejected'; - break; - case 10: - color = 'success'; - icon = ; - label = 'Released'; - break; - default: - color = 'default'; - icon = ; - label = 'Unknown'; - break; + case 0: return { ...warn, icon: , label: 'Under Construction' }; + case 1: return { ...warn, icon: , label: 'Changes Requested' }; + case 2: return { ...info, icon: , label: 'Submitting' }; + case 3: return { ...warn, icon: , label: 'Under Review' }; + case 4: return { ...warn, icon: , label: 'Script Review' }; + case 5: return { ...info, icon: , label: 'Validating' }; + case 6: return { ...success, icon: , label: 'Validated' }; + case 7: return { ...info, icon: , label: 'Uploading' }; + case 8: return { ...success, icon: , label: 'Uploaded' }; + case 9: return { ...error, icon: , label: 'Rejected' }; + case 10: return { ...success, icon: , label: 'Released' }; + case 11: return { ...info, icon: , label: 'Releasing' }; + default: return { ...gray, icon: , label: 'Unknown' }; } +} + +export const StatusChip = ({status}: { status: number }): JSX.Element => { + const config = getStatusConfig(status); return ( ); }; diff --git a/web/src/app/_components/webpage.tsx b/web/src/app/_components/webpage.tsx index 3ee1d52..46b3029 100644 --- a/web/src/app/_components/webpage.tsx +++ b/web/src/app/_components/webpage.tsx @@ -1,8 +1,13 @@ +import { Box } from "@mui/material"; import Header from "./header"; +import AnimatedBackground from "./AnimatedBackground"; export default function Webpage({children}: Readonly<{children?: React.ReactNode}>) { return <> -
- {children} + + +
+ {children} + } diff --git a/web/src/app/admin-submit/page.scss b/web/src/app/admin-submit/page.scss index 5db80da..20d52d8 100644 --- a/web/src/app/admin-submit/page.scss +++ b/web/src/app/admin-submit/page.scss @@ -24,10 +24,10 @@ color: var(--text-color); } & fieldset { - border-color: rgb(100,100,100); + border-color: rgba(255, 255, 255, 0.1); } & span { - color: white; + color: #fafafa; } } diff --git a/web/src/app/globals.scss b/web/src/app/globals.scss index b53e547..ffbb8fb 100644 --- a/web/src/app/globals.scss +++ b/web/src/app/globals.scss @@ -1,37 +1,18 @@ -$review-border: 1px solid var(--review-border); +$review-border: 1px solid rgba(255, 255, 255, 0.06); $form-label-fontsize: 1.3rem; @mixin border-with-radius { border: $review-border { - radius: 5px; + radius: 8px; } } :root { color-scheme: dark; - --header-height: 45px; - - --page: rgb(15,15,15); - --header-grad-left: #363b40; - --header-grad-right: #353a40; - --header-button-left: white; - --header-button-right: #b4b4b4; - --header-button-hover: white; - --review-border: rgb(50,50,50); - --text-color: rgb(230,230,230); - --anchor-link-review: #008fd6; - --window-header: rgb(10,10,10); - --comment-highlighted: #ffffd7; - --comment-area: rgb(20,20,20); - --placeholder-text: rgb(80,80,80); -} - -body { - font-family: -apple-system, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla"; - box-sizing: border-box; - margin: 0; - background-color: var(--page); + --review-border: rgba(255, 255, 255, 0.06); + --text-color: #fafafa; + --placeholder-text: #52525b; } button { @@ -41,10 +22,3 @@ button { a:active, a:link, a:hover { text-decoration: none; } - -.spacer { - display: block; - width: 100%; - height: 1px; - background-color: var(--review-border); -} \ No newline at end of file diff --git a/web/src/app/lib/colors.ts b/web/src/app/lib/colors.ts new file mode 100644 index 0000000..3c568ef --- /dev/null +++ b/web/src/app/lib/colors.ts @@ -0,0 +1,88 @@ +// Brand / accent +export const primary = { + main: '#a78bfa', + light: '#c4b5fd', + dark: '#7c3aed', + darker: '#6d28d9', + mid: '#8b5cf6', +} as const; + +export const secondary = { + main: '#22d3ee', + light: '#67e8f9', + dark: '#0891b2', +} as const; + +// Semantic +export const semantic = { + error: '#f87171', + warning: '#fbbf24', + success: '#4ade80', + info: '#38bdf8', +} as const; + +// Surfaces +export const surface = { + base: '#09090b', + raised: '#18181b', + raisedAlpha: 'rgba(24, 24, 27, 0.6)', + raisedSolid: 'rgba(24, 24, 27, 0.95)', + overlay: 'rgba(0, 0, 0, 0.6)', + appBar: 'rgba(9, 9, 11, 0.8)', +} as const; + +// Text hierarchy (lightest -> dimmest) +export const text = { + primary: '#fafafa', + secondary: '#d4d4d8', + tertiary: '#a1a1aa', + muted: '#71717a', + dim: '#52525b', + faint: '#3f3f46', +} as const; + +// Borders & dividers +export const border = { + subtle: 'rgba(255, 255, 255, 0.04)', + default: 'rgba(255, 255, 255, 0.06)', + medium: 'rgba(255, 255, 255, 0.08)', + strong: 'rgba(255, 255, 255, 0.1)', + primarySubtle: 'rgba(167, 139, 250, 0.1)', + primaryDefault: 'rgba(167, 139, 250, 0.15)', + primaryMedium: 'rgba(167, 139, 250, 0.2)', + primaryStrong: 'rgba(167, 139, 250, 0.3)', +} as const; + +// Interactive surface fills +export const fill = { + subtle: 'rgba(255, 255, 255, 0.03)', + default: 'rgba(255, 255, 255, 0.04)', + hover: 'rgba(255, 255, 255, 0.06)', + primaryHover: 'rgba(167, 139, 250, 0.06)', + primaryActive: 'rgba(167, 139, 250, 0.08)', + primaryStrong: 'rgba(167, 139, 250, 0.1)', +} as const; + +// Gradient presets +export const gradients = { + brand: `linear-gradient(135deg, ${primary.dark}, ${secondary.main})`, + brandText: `linear-gradient(135deg, ${primary.light} 0%, ${secondary.main} 100%)`, + button: `linear-gradient(135deg, ${primary.dark} 0%, ${primary.main} 100%)`, + buttonHover: `linear-gradient(135deg, ${primary.darker} 0%, ${primary.mid} 100%)`, + titleText: `linear-gradient(135deg, ${text.primary} 0%, ${text.tertiary} 100%)`, +} as const; + +// Glow / shadow presets +export const glow = { + brand: '0 0 12px rgba(124, 58, 237, 0.5)', + brandStrong: '0 0 20px rgba(124, 58, 237, 0.8), 0 0 40px rgba(34, 211, 238, 0.2)', + button: '0 0 20px rgba(124, 58, 237, 0.3)', + palette: '0 24px 80px rgba(0, 0, 0, 0.5), 0 0 60px rgba(124, 58, 237, 0.1)', +} as const; + +// Game colors +export const gameColors: Record = { + 1: '#a78bfa', // Bhop - purple + 2: '#22d3ee', // Surf - cyan + 5: '#fbbf24', // Fly Trials - yellow +} as const; diff --git a/web/src/app/lib/theme.tsx b/web/src/app/lib/theme.tsx index 079d60d..3a2c2b4 100644 --- a/web/src/app/lib/theme.tsx +++ b/web/src/app/lib/theme.tsx @@ -1,133 +1,263 @@ -import {createTheme} from "@mui/material"; +import { createTheme } from '@mui/material'; +import { primary, secondary, semantic, surface, text, border, fill, gradients, glow } from './colors'; export const theme = createTheme({ - cssVariables: { - colorSchemeSelector: 'class', - }, - colorSchemes: { - dark: true, - }, - defaultColorScheme: 'dark', palette: { mode: 'dark', primary: { - main: '#3b82f6', - dark: '#2563eb', - light: '#60a5fa', + main: primary.main, + light: primary.light, + dark: primary.dark, }, secondary: { - main: '#8b5cf6', - dark: '#7c3aed', - light: '#a78bfa', + main: secondary.main, + light: secondary.light, + dark: secondary.dark, }, background: { - default: '#0a0a0a', - paper: '#171717', + default: surface.base, + paper: surface.raised, }, + error: { main: semantic.error }, + warning: { main: semantic.warning }, + success: { main: semantic.success }, + info: { main: semantic.info }, text: { - primary: '#ffffff', - secondary: '#9ca3af', - }, - error: { - main: '#ef4444', - light: '#f87171', - dark: '#dc2626', - }, - warning: { - main: '#f59e0b', - light: '#fbbf24', - dark: '#d97706', - }, - success: { - main: '#10b981', - light: '#34d399', - dark: '#059669', - }, - info: { - main: '#3b82f6', - light: '#60a5fa', - dark: '#2563eb', - }, - }, - typography: { - fontFamily: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", sans-serif', - h1: { - fontWeight: 700, - letterSpacing: '-0.025em', - }, - h2: { - fontWeight: 700, - letterSpacing: '-0.02em', - }, - h3: { - fontWeight: 600, - letterSpacing: '-0.015em', - }, - h4: { - fontWeight: 600, - letterSpacing: '-0.01em', - }, - h5: { - fontWeight: 600, - }, - h6: { - fontWeight: 600, - }, - subtitle1: { - fontWeight: 500, - fontSize: '1rem', - }, - body1: { - fontSize: '1rem', - lineHeight: 1.7, - }, - body2: { - fontSize: '0.875rem', - lineHeight: 1.6, - }, - caption: { - fontSize: '0.75rem', - }, - button: { - fontWeight: 600, - textTransform: 'none', - letterSpacing: '0.01em', + primary: text.primary, + secondary: text.tertiary, }, + divider: border.default, }, shape: { borderRadius: 12, }, + typography: { + fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif', + fontSize: 14, + h1: { fontWeight: 700, letterSpacing: '-0.025em' }, + h2: { fontWeight: 700, letterSpacing: '-0.02em' }, + h3: { fontWeight: 600, letterSpacing: '-0.015em' }, + h4: { fontWeight: 700, letterSpacing: '-0.02em' }, + h5: { fontWeight: 700, letterSpacing: '-0.02em' }, + h6: { fontWeight: 700, letterSpacing: '-0.01em' }, + body1: { fontSize: '1rem', lineHeight: 1.7 }, + body2: { fontSize: '0.875rem', lineHeight: 1.6 }, + button: { fontWeight: 600, textTransform: 'none' as const }, + }, components: { + MuiCssBaseline: { + styleOverrides: { + body: { + backgroundColor: surface.base, + backgroundImage: 'radial-gradient(ellipse 80% 50% at 50% -20%, rgba(120, 60, 255, 0.15), transparent)', + }, + }, + }, + MuiAppBar: { + styleOverrides: { + root: { + backgroundColor: surface.appBar, + backdropFilter: 'blur(16px)', + boxShadow: 'none', + borderBottom: `1px solid ${border.default}`, + }, + }, + }, + MuiPaper: { + styleOverrides: { + root: { + backgroundImage: 'none', + backgroundColor: surface.raisedAlpha, + backdropFilter: 'blur(12px)', + border: `1px solid ${border.default}`, + }, + }, + }, MuiCard: { styleOverrides: { root: { borderRadius: 12, overflow: 'hidden', - backgroundColor: '#171717', - border: '1px solid rgba(255, 255, 255, 0.08)', - boxShadow: '0 1px 3px rgba(0, 0, 0, 0.3)', + backgroundColor: surface.raisedAlpha, + backdropFilter: 'blur(12px)', + border: `1px solid ${border.default}`, + boxShadow: 'none', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', '&:hover': { transform: 'translateY(-4px)', - border: '1px solid rgba(59, 130, 246, 0.4)', - boxShadow: '0 8px 24px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(59, 130, 246, 0.2)', + border: `1px solid ${border.primaryMedium}`, + boxShadow: glow.button, }, }, }, }, - MuiCardMedia: { - styleOverrides: { - root: { - transition: 'transform 0.3s', - }, - }, - }, MuiCardContent: { styleOverrides: { root: { padding: 16, - '&:last-child': { - paddingBottom: 16, + '&:last-child': { paddingBottom: 16 }, + }, + }, + }, + MuiTableContainer: { + styleOverrides: { + root: { + backgroundImage: 'none', + borderRadius: 12, + }, + }, + }, + MuiTableHead: { + styleOverrides: { + root: { + '& .MuiTableCell-head': { + backgroundColor: fill.subtle, + color: text.tertiary, + fontWeight: 600, + fontSize: '0.75rem', + textTransform: 'uppercase' as const, + letterSpacing: '0.05em', + borderBottom: `1px solid ${border.default}`, + padding: '12px 16px', + }, + }, + }, + }, + MuiTableBody: { + styleOverrides: { + root: { + '& .MuiTableRow-root': { + transition: 'background-color 0.15s ease', + '&:hover': { + backgroundColor: fill.primaryHover, + }, + }, + '& .MuiTableCell-body': { + borderBottom: `1px solid ${border.subtle}`, + padding: '14px 16px', + fontSize: '0.875rem', + color: text.secondary, + }, + }, + }, + }, + MuiTab: { + styleOverrides: { + root: { + textTransform: 'none' as const, + fontWeight: 500, + fontSize: '0.875rem', + minHeight: 44, + '&.Mui-selected': { color: primary.main }, + }, + }, + }, + MuiTabs: { + styleOverrides: { + indicator: { + backgroundColor: primary.main, + height: 2, + borderRadius: 1, + }, + }, + }, + MuiButton: { + styleOverrides: { + root: { + textTransform: 'none' as const, + fontWeight: 600, + borderRadius: 8, + }, + contained: { + boxShadow: 'none', + }, + containedPrimary: { + background: gradients.button, + '&:hover': { + boxShadow: glow.button, + background: gradients.buttonHover, + }, + }, + containedSuccess: { + backgroundColor: semantic.success, + color: '#000', + '&:hover': { + backgroundColor: '#22c55e', + boxShadow: '0 0 16px rgba(74, 222, 128, 0.3)', + }, + }, + containedError: { + backgroundColor: semantic.error, + '&:hover': { + backgroundColor: '#ef4444', + boxShadow: '0 0 16px rgba(248, 113, 113, 0.3)', + }, + }, + containedWarning: { + backgroundColor: semantic.warning, + color: '#000', + '&:hover': { + backgroundColor: '#f59e0b', + boxShadow: '0 0 16px rgba(251, 191, 36, 0.3)', + }, + }, + containedInfo: { + backgroundColor: semantic.info, + '&:hover': { + backgroundColor: '#0ea5e9', + boxShadow: '0 0 16px rgba(56, 189, 248, 0.3)', + }, + }, + outlined: { + borderColor: border.primaryStrong, + color: primary.main, + '&:hover': { + borderColor: primary.main, + backgroundColor: fill.primaryActive, + }, + }, + outlinedSuccess: { + borderColor: `rgba(74, 222, 128, 0.4)`, + color: semantic.success, + '&:hover': { + borderColor: semantic.success, + backgroundColor: 'rgba(74, 222, 128, 0.08)', + }, + }, + outlinedError: { + borderColor: `rgba(248, 113, 113, 0.4)`, + color: semantic.error, + '&:hover': { + borderColor: semantic.error, + backgroundColor: 'rgba(248, 113, 113, 0.08)', + }, + }, + outlinedWarning: { + borderColor: `rgba(251, 191, 36, 0.4)`, + color: semantic.warning, + '&:hover': { + borderColor: semantic.warning, + backgroundColor: 'rgba(251, 191, 36, 0.08)', + }, + }, + outlinedInfo: { + borderColor: `rgba(56, 189, 248, 0.4)`, + color: semantic.info, + '&:hover': { + borderColor: semantic.info, + backgroundColor: 'rgba(56, 189, 248, 0.08)', + }, + }, + }, + }, + MuiTextField: { + styleOverrides: { + root: { + '& .MuiOutlinedInput-root': { + borderRadius: 8, + '& fieldset': { borderColor: border.strong }, + '&:hover fieldset': { borderColor: border.primaryStrong }, + '&.Mui-focused fieldset': { borderColor: primary.main }, }, }, }, @@ -136,134 +266,39 @@ export const theme = createTheme({ styleOverrides: { root: { fontWeight: 600, - borderRadius: 6, fontSize: '0.75rem', - transition: 'all 0.2s ease-in-out', - }, - icon: { - marginLeft: '8px', - }, - colorError: { - backgroundColor: '#ef4444', - color: '#ffffff', - '& .MuiChip-icon': { - color: '#ffffff', - }, - }, - colorWarning: { - backgroundColor: '#f59e0b', - color: '#ffffff', - '& .MuiChip-icon': { - color: '#ffffff', - }, - }, - colorSuccess: { - backgroundColor: '#10b981', - color: '#ffffff', - '& .MuiChip-icon': { - color: '#ffffff', - }, - }, - colorInfo: { - backgroundColor: '#3b82f6', - color: '#ffffff', - '& .MuiChip-icon': { - color: '#ffffff', - }, + borderRadius: 6, }, }, }, MuiDivider: { styleOverrides: { root: { - borderColor: 'rgba(148, 163, 184, 0.1)', - }, - }, - }, - MuiPaper: { - styleOverrides: { - root: { - backgroundImage: 'none', - backgroundColor: '#171717', - }, - }, - }, - MuiButton: { - styleOverrides: { - root: { - borderRadius: 8, - fontWeight: 600, - textTransform: 'none', - padding: '10px 24px', - transition: 'all 0.2s ease-in-out', - }, - contained: { - boxShadow: 'none', - '&:hover': { - boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)', - transform: 'translateY(-1px)', - }, - }, - containedPrimary: { - background: '#3b82f6', - '&:hover': { - background: '#2563eb', - }, - }, - outlined: { - borderWidth: '1.5px', - '&:hover': { - borderWidth: '1.5px', - backgroundColor: 'rgba(59, 130, 246, 0.08)', - }, - }, - outlinedPrimary: { - borderColor: 'rgba(59, 130, 246, 0.5)', - '&:hover': { - borderColor: '#3b82f6', - backgroundColor: 'rgba(59, 130, 246, 0.08)', - }, - }, - outlinedSecondary: { - borderColor: 'rgba(139, 92, 246, 0.5)', - '&:hover': { - borderColor: '#8b5cf6', - backgroundColor: 'rgba(139, 92, 246, 0.08)', - }, - }, - }, - }, - MuiAppBar: { - styleOverrides: { - root: { - background: 'rgba(10, 10, 10, 0.8)', - backdropFilter: 'blur(12px)', - borderBottom: '1px solid rgba(255, 255, 255, 0.08)', - boxShadow: 'none', + borderColor: border.default, }, }, }, MuiDrawer: { styleOverrides: { paper: { - backgroundColor: '#0a0a0a', - borderRight: '1px solid rgba(255, 255, 255, 0.08)', + backgroundColor: surface.base, + borderRight: `1px solid ${border.default}`, }, }, }, MuiCircularProgress: { styleOverrides: { root: { - color: '#3b82f6', + color: primary.main, }, }, }, MuiIconButton: { styleOverrides: { root: { - transition: 'all 0.2s ease-in-out', + transition: 'all 0.15s ease', '&:hover': { - backgroundColor: 'rgba(59, 130, 246, 0.1)', + backgroundColor: fill.primaryActive, }, }, }, @@ -271,11 +306,11 @@ export const theme = createTheme({ MuiLink: { styleOverrides: { root: { - color: '#60a5fa', + color: primary.light, textDecoration: 'none', - transition: 'color 0.2s ease-in-out', + transition: 'color 0.15s ease', '&:hover': { - color: '#3b82f6', + color: primary.main, textDecoration: 'underline', }, }, @@ -284,28 +319,28 @@ export const theme = createTheme({ MuiMenu: { styleOverrides: { paper: { - background: '#171717', - backdropFilter: 'blur(12px)', - border: '1px solid rgba(255, 255, 255, 0.08)', - boxShadow: '0 4px 12px rgba(0, 0, 0, 0.4)', + background: surface.raisedSolid, + backdropFilter: 'blur(16px)', + border: `1px solid ${border.default}`, + boxShadow: glow.palette, }, }, }, MuiMenuItem: { styleOverrides: { root: { - transition: 'all 0.2s ease-in-out', + transition: 'all 0.15s ease', '&:hover': { - backgroundColor: 'rgba(59, 130, 246, 0.1)', + backgroundColor: fill.primaryHover, }, '&.Mui-selected': { - backgroundColor: 'rgba(59, 130, 246, 0.15)', + backgroundColor: fill.primaryActive, '&:hover': { - backgroundColor: 'rgba(59, 130, 246, 0.2)', + backgroundColor: fill.primaryStrong, }, }, }, }, }, }, -}); \ No newline at end of file +}); diff --git a/web/src/app/maps/[mapId]/page.tsx b/web/src/app/maps/[mapId]/page.tsx index ad93bca..7bb490d 100644 --- a/web/src/app/maps/[mapId]/page.tsx +++ b/web/src/app/maps/[mapId]/page.tsx @@ -391,12 +391,12 @@ export default function MapDetails() { px: 2, borderRadius: 1, transition: 'all 0.2s', - backgroundColor: 'rgba(25, 118, 210, 0.08)', + backgroundColor: 'rgba(167, 139, 250, 0.08)', borderLeft: '4px solid', borderColor: 'primary.main', mb: releasedFixes.length > 0 ? 2 : 0, '&:hover': { - backgroundColor: 'rgba(25, 118, 210, 0.12)', + backgroundColor: 'rgba(167, 139, 250, 0.12)', transform: 'translateX(4px)' }, textDecoration: 'none', diff --git a/web/src/app/not-found/page.tsx b/web/src/app/not-found/page.tsx index 02eea1c..44522ab 100644 --- a/web/src/app/not-found/page.tsx +++ b/web/src/app/not-found/page.tsx @@ -4,6 +4,7 @@ import Webpage from "@/app/_components/webpage"; import { useTitle } from "@/app/hooks/useTitle"; import HomeIcon from "@mui/icons-material/Home"; import MapIcon from "@mui/icons-material/Map"; +import { semantic, surface } from "@/app/lib/colors"; export default function NotFound() { useTitle("404 - Page Not Found"); @@ -19,7 +20,7 @@ export default function NotFound() { display: 'flex', alignItems: 'center', overflow: 'hidden', - background: 'linear-gradient(to bottom, #0a0a0a 0%, #0f0f0f 100%)', + background: `linear-gradient(to bottom, ${surface.base} 0%, #0f0f0f 100%)`, }} > {/* Subtle Gradient Background */} @@ -30,7 +31,7 @@ export default function NotFound() { right: '30%', width: '500px', height: '500px', - background: 'radial-gradient(circle, rgba(239, 68, 68, 0.1) 0%, transparent 70%)', + background: `radial-gradient(circle, rgba(248, 113, 113, 0.1) 0%, transparent 70%)`, borderRadius: '50%', filter: 'blur(80px)', }} @@ -42,7 +43,7 @@ export default function NotFound() { left: '25%', width: '450px', height: '450px', - background: 'radial-gradient(circle, rgba(59, 130, 246, 0.08) 0%, transparent 70%)', + background: `radial-gradient(circle, rgba(167, 139, 250, 0.08) 0%, transparent 70%)`, borderRadius: '50%', filter: 'blur(80px)', }} @@ -59,7 +60,7 @@ export default function NotFound() { lineHeight: 1, mb: 2, letterSpacing: '-0.04em', - background: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)', + background: `linear-gradient(135deg, ${semantic.error} 0%, #dc2626 100%)`, WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text', @@ -173,7 +174,7 @@ export default function NotFound() { fontSize: '1rem', '&:hover': { color: 'primary.main', - background: 'rgba(59, 130, 246, 0.1)', + background: `rgba(167, 139, 250, 0.1)`, }, }} > diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx index f5ec29f..04a4bf8 100644 --- a/web/src/app/page.tsx +++ b/web/src/app/page.tsx @@ -26,6 +26,7 @@ import RocketLaunchIcon from "@mui/icons-material/RocketLaunch"; import EmojiEventsIcon from "@mui/icons-material/EmojiEvents"; import { useUser } from "@/app/hooks/useUser"; import { hasAnyReviewerRole } from "@/app/ts/Roles"; +import { primary, secondary, semantic, text, border, fill, gradients, glow } from "@/app/lib/colors"; export default function Home() { useTitle("Home"); @@ -167,8 +168,8 @@ export default function Home() { }} > - - + + Loading content... @@ -223,32 +224,32 @@ export default function Home() { value: totalSubmissions, label: 'Total Submissions', sublabel: 'Total maps submitted by the community', - color: '#3b82f6', - gradient: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)', + color: primary.main, + gradient: gradients.button, }, { icon: , value: totalMapfixes, label: 'Total Map Fixes', sublabel: 'Total map fixes submitted by the community', - color: '#8b5cf6', - gradient: 'linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%)', + color: secondary.main, + gradient: `linear-gradient(135deg, ${secondary.dark} 0%, ${secondary.main} 100%)`, }, { icon: , value: releasedSubmissions + releasedMapfixes, label: 'Total Released', sublabel: 'Maps & fixes that have been released to the game', - color: '#10b981', - gradient: 'linear-gradient(135deg, #10b981 0%, #059669 100%)', + color: semantic.success, + gradient: `linear-gradient(135deg, ${semantic.success} 0%, #059669 100%)`, }, { icon: , value: releasedSubmissions, label: 'Released Submissions', sublabel: 'Approved maps that have been published to the game', - color: '#10b981', - gradient: 'linear-gradient(135deg, #10b981 0%, #059669 100%)', + color: semantic.success, + gradient: `linear-gradient(135deg, ${semantic.success} 0%, #059669 100%)`, }, { icon: , @@ -263,8 +264,8 @@ export default function Home() { value: submittedSubmissions + submittedMapfixes, label: 'Under Review', sublabel: 'Pending approval fixes & submissions', - color: '#f59e0b', - gradient: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)', + color: semantic.warning, + gradient: `linear-gradient(135deg, ${semantic.warning} 0%, #d97706 100%)`, }, ]; @@ -284,8 +285,8 @@ export default function Home() { }} > - - + + Loading... @@ -305,7 +306,7 @@ export default function Home() { return ( - + {/* Hero Section */} - {/* Animated Background Elements */} + {/* Animated Background Orbs */} @@ -387,7 +387,7 @@ export default function Home() { left: 0, right: 0, height: '1px', - background: 'linear-gradient(90deg, transparent 0%, rgba(139, 92, 246, 0.3) 50%, transparent 100%)', + background: `linear-gradient(90deg, transparent 0%, rgba(34, 211, 238, 0.2) 50%, transparent 100%)`, opacity: 0.5, }} /> @@ -412,7 +412,7 @@ export default function Home() { fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', - color: 'primary.main', + color: primary.main, mb: 3, display: 'block', opacity: 0.9, @@ -424,16 +424,16 @@ export default function Home() { StrafesNET @@ -442,7 +442,7 @@ export default function Home() { - {/* CTA Buttons - Moved up for better hierarchy */} + {/* CTA Buttons */} - {/* Stats Section - Completely Redesigned */} + {/* Stats Section */} - {/* Stats Grid */} - {/* Icon */} - {/* Value */} - {/* Label */} - {/* Featured Stat Description */} Discover the newest custom maps created by the community @@ -740,8 +717,7 @@ export default function Home() { Community-created map fixes and improvements @@ -771,8 +747,7 @@ export default function Home() { Join the community and start contributing today @@ -791,21 +766,21 @@ export default function Home() { title: 'Submit Maps', description: 'Upload your custom bhop and surf maps for review. Maps are evaluated by moderators before being added to the game.', link: '/submit', - color: '#3b82f6', + color: primary.main, }, { icon: , title: 'Submit Fixes', description: 'Found bugs or issues in existing maps? Submit fixed versions to improve map quality for all players.', link: '/mapfixes', - color: '#8b5cf6', + color: secondary.main, }, { icon: , title: 'View Submissions', description: 'Browse all pending and approved submissions currently in the review queue. Track submission status and feedback.', link: '/submissions', - color: '#10b981', + color: semantic.success, }, ].map((card, index) => ( @@ -874,8 +850,7 @@ export default function Home() { sx={{ position: 'relative', py: 12, - background: '#0f0f0f', - borderTop: '1px solid rgba(255, 255, 255, 0.08)', + borderTop: `1px solid ${border.default}`, }} > @@ -893,7 +868,7 @@ export default function Home() { { switch (type) { case 'warning': - return { bg: 'rgba(250, 200, 90, 0.15)', border: '#fac85a', color: '#fac85a' }; + return { bg: `rgba(251, 191, 36, 0.15)`, border: semantic.warning, color: semantic.warning }; case 'error': - return { bg: 'rgba(240, 82, 82, 0.15)', border: '#f05252', color: '#f05252' }; + return { bg: `rgba(248, 113, 113, 0.15)`, border: semantic.error, color: semantic.error }; case 'success': - return { bg: 'rgba(80, 200, 120, 0.15)', border: '#50c878', color: '#50c878' }; + return { bg: `rgba(74, 222, 128, 0.15)`, border: semantic.success, color: semantic.success }; default: - return { bg: 'rgba(100, 150, 230, 0.15)', border: '#6496e6', color: '#6496e6' }; + return { bg: `rgba(167, 139, 250, 0.15)`, border: primary.main, color: primary.main }; } }; @@ -636,15 +637,15 @@ export default function ScriptReviewPage() { - Loading script... + Loading script... ); @@ -703,13 +704,13 @@ export default function ScriptReviewPage() { height: '100vh', display: 'flex', flexDirection: 'column', - bgcolor: '#1e1e1e', + bgcolor: surface.raised, overflow: 'hidden', }}> {/* Title Bar */} - - + + Script Review @@ -732,7 +733,7 @@ export default function ScriptReviewPage() { > Previous - + {currentIndex + 1} / {allScripts.length} - - + + UNSAVED CHANGES @@ -787,16 +788,16 @@ export default function ScriptReviewPage() { width: 300, minWidth: 300, flexShrink: 0, - bgcolor: '#252526', - borderRight: '1px solid #2b2b2c', + bgcolor: surface.raised, + borderRight: `1px solid ${border.default}`, display: 'flex', flexDirection: 'column', overflow: 'auto', }}> {/* Script Info Section */} - + - + Name - + Hash {/* Policy Selection Section */} - + { if (!submitting && !sourceChanged) { - e.currentTarget.style.backgroundColor = '#9d0f0f'; + e.currentTarget.style.backgroundColor = '#991b1b'; } }} onMouseLeave={(e) => { if (!submitting && !sourceChanged) { - e.currentTarget.style.backgroundColor = '#7e0e0e'; + e.currentTarget.style.backgroundColor = '#7f1d1d'; } }} onMouseDown={(e) => { if (!submitting && !sourceChanged) { - e.currentTarget.style.backgroundColor = '#b81414'; + e.currentTarget.style.backgroundColor = semantic.error; } }} onMouseUp={(e) => { if (!submitting && !sourceChanged) { - e.currentTarget.style.backgroundColor = '#9d0f0f'; + e.currentTarget.style.backgroundColor = '#991b1b'; } }} > @@ -976,15 +977,15 @@ export default function ScriptReviewPage() { - + ⚠️ Permanent Deletion - + This will permanently delete the script and policy. This action cannot be undone. @@ -1021,21 +1022,21 @@ export default function ScriptReviewPage() { {/* Tab Bar */} @@ -1048,7 +1049,7 @@ export default function ScriptReviewPage() { width: 8, height: 8, borderRadius: '50%', - bgcolor: '#f59e0b' + bgcolor: semantic.warning }} /> )} @@ -1113,7 +1114,7 @@ export default function ScriptReviewPage() { {/* Status Bar */} {snackbar.message} diff --git a/web/src/app/user-dashboard/page.tsx b/web/src/app/user-dashboard/page.tsx index 4eb5549..c51ff37 100644 --- a/web/src/app/user-dashboard/page.tsx +++ b/web/src/app/user-dashboard/page.tsx @@ -6,6 +6,7 @@ import Webpage from "@/app/_components/webpage"; import { ListSortConstants } from "../ts/Sort"; import { hasAnyReviewerRole } from "../ts/Roles"; import { useUser } from "@/app/hooks/useUser"; +import { primary, semantic } from "@/app/lib/colors"; import { Box, Breadcrumbs, @@ -407,7 +408,7 @@ export default function UserDashboardPage() { gap: 2, mb: 4 }}> - + Total Contributions @@ -422,7 +423,7 @@ export default function UserDashboardPage() { - + Released @@ -437,7 +438,7 @@ export default function UserDashboardPage() { - + In Review @@ -452,7 +453,7 @@ export default function UserDashboardPage() { - + Action Needed diff --git a/web/src/app/utils/games.ts b/web/src/app/utils/games.ts index 729b42b..8123336 100644 --- a/web/src/app/utils/games.ts +++ b/web/src/app/utils/games.ts @@ -16,17 +16,17 @@ export function getGameInfo(gameId: number) { case 1: return { name: "Bhop", - color: "#2196f3" // blue + color: "#a78bfa" // purple }; case 2: return { name: "Surf", - color: "#4caf50" // green + color: "#22d3ee" // cyan }; case 5: return { name: "Fly Trials", - color: "#ff9800" // orange + color: "#fbbf24" // yellow }; default: return {