Add dark mode, theme, and UI polish across the site

Co-authored-by: 20kaushik02 <59662438+20kaushik02@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-08-22 07:33:36 +00:00
committed by GitHub
parent de42149c8c
commit 6717cbb30e
6 changed files with 125 additions and 23 deletions

View File

@@ -3,8 +3,17 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="theme-color" content="#00695f" />
<meta name="description" content="Kaushik's portfolio website" />
<meta property="og:title" content="Kaushik's Website" />
<meta property="og:description" content="Kaushik's portfolio website" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://knravish.me" />
<meta property="og:image" content="%PUBLIC_URL%/android-chrome-512x512.png" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="Kaushik's Website" />
<meta name="twitter:description" content="Kaushik's portfolio website" />
<meta name="twitter:image" content="%PUBLIC_URL%/android-chrome-512x512.png" />
<link
rel="apple-touch-icon"
sizes="180x180"
@@ -29,7 +38,6 @@
color="#5bbad5"
/>
<meta name="msapplication-TileColor" content="#00a300" />
<meta name="theme-color" content="#ffffff" />
<title>Kaushik's Website</title>
</head>
<body>

View File

@@ -1,10 +1,28 @@
import { createContext, useEffect, useState } from 'react';
import { createContext, useEffect, useMemo, useState } from 'react';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import Menu from './components/Menu';
import { getAppTheme } from './theme';
export const WidthContext = createContext();
export const ColorModeContext = createContext({ mode: 'light', toggleMode: () => { } });
const THEME_STORAGE_KEY = 'colorMode';
function getInitialMode() {
const stored = window.localStorage.getItem(THEME_STORAGE_KEY);
if (stored === 'light' || stored === 'dark') {
return stored;
}
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
}
function App() {
const [width, setWidth] = useState(0);
const [mode, setMode] = useState(getInitialMode);
// Get window dimensions on resize
useEffect(() => {
window.addEventListener("resize", updateWindowDimensions);
@@ -20,10 +38,29 @@ function App() {
useEffect(() => {
updateWindowDimensions();
}, []);
const colorMode = useMemo(() => ({
mode,
toggleMode: () => {
setMode((prevMode) => {
const nextMode = prevMode === 'light' ? 'dark' : 'light';
window.localStorage.setItem(THEME_STORAGE_KEY, nextMode);
return nextMode;
});
},
}), [mode]);
const theme = useMemo(() => getAppTheme(mode), [mode]);
return (
<WidthContext.Provider value={width}>
<Menu />
</WidthContext.Provider>
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<CssBaseline />
<WidthContext.Provider value={width}>
<Menu />
</WidthContext.Provider>
</ThemeProvider>
</ColorModeContext.Provider>
);
}

View File

@@ -16,14 +16,14 @@ const socialsData = [
const Landing = () => {
return (
<>
<Typography variant="h4" p={2} align="left">
Hi, I'm Kaushik 👋
</Typography>
<List>
{/* summary */}
<ListItem>
<ListItemText
primary={"Welcome to my (messy) homepage. I like data!\
My passions lie in data engineering/analytics, distributed technologies,\
networks and security, web development, machine learning...\
you get the picture." } />
primary={"Welcome to my (messy) homepage. I like data! My passions lie in data engineering/analytics, distributed technologies, networks and security, web development, machine learning... you get the picture."} />
</ListItem>
{/* current endeavor */}
<ListItem>

View File

@@ -3,7 +3,6 @@ import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Avatar from '@mui/material/Avatar';
import Box from '@mui/material/Box';
import CssBaseline from '@mui/material/CssBaseline';
import Divider from '@mui/material/Divider';
import Drawer from '@mui/material/Drawer';
import IconButton from '@mui/material/IconButton';
@@ -16,6 +15,7 @@ import MenuIcon from '@mui/icons-material/Menu';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Link from '@mui/material/Link';
import Tooltip from '@mui/material/Tooltip';
import LandingIcon from '@mui/icons-material/Home';
import ProjectsIcon from '@mui/icons-material/AccountTree';
@@ -23,6 +23,8 @@ import CareerIcon from '@mui/icons-material/Work';
import InterestsIcon from '@mui/icons-material/Headphones';
import ArticleIcon from '@mui/icons-material/Article';
import BlogIcon from '@mui/icons-material/RateReview';
import LightModeIcon from '@mui/icons-material/LightMode';
import DarkModeIcon from '@mui/icons-material/DarkMode';
import Landing from './Landing';
// import LoremIpsum from './LoremIpsum';
@@ -30,6 +32,7 @@ import Projects from './Projects';
import Career from './Career';
import { handleLinkClick } from '../utils/linkClick';
import UnderConstruction from './UnderConstruction';
import { ColorModeContext } from '../App';
const drawerWidth = 240;
@@ -64,6 +67,11 @@ function Menu(props) {
const [mobileOpen, setMobileOpen] = React.useState(false);
const [isClosing, setIsClosing] = React.useState(false);
const [activeSection, setActiveSection] = React.useState(menuSections[0]);
const { mode, toggleMode } = React.useContext(ColorModeContext);
React.useEffect(() => {
document.title = `Kaushik | ${activeSection.display_name}`;
}, [activeSection]);
const handleDrawerClose = () => {
setIsClosing(true);
@@ -130,7 +138,6 @@ function Menu(props) {
return (
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<AppBar
position="fixed"
sx={{
@@ -148,9 +155,14 @@ function Menu(props) {
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>
{activeSection.appbar_text}
</Typography>
<Tooltip title={mode === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}>
<IconButton color="inherit" aria-label="toggle color mode" onClick={toggleMode}>
{mode === 'dark' ? <LightModeIcon /> : <DarkModeIcon />}
</IconButton>
</Tooltip>
</Toolbar>
</AppBar>
<Box

View File

@@ -1,7 +1,9 @@
import React, { useContext } from 'react';
import { Box, Button, Card, CardActionArea, CardActions, CardContent, CardMedia, Chip, List, ListItem, ListItemText, Typography } from '@mui/material';
import { Box, Button, Card, CardActionArea, CardActions, CardContent, CardMedia, Chip, List, ListItem, ListItemText, Stack, Typography } from '@mui/material';
import { Swiper, SwiperSlide } from 'swiper/react';
import "swiper/css";
import "swiper/css/pagination";
import "swiper/css/navigation";
import { Pagination, Navigation, EffectFade } from "swiper/modules";
import { WidthContext } from '../App';
@@ -82,8 +84,8 @@ const Projects = () => {
const width = useContext(WidthContext);
const ProjectCard = ({ project }) => {
return (
<Card sx={{ maxWidth: 400 }}>
<CardActionArea>
<Card sx={{ maxWidth: 400, height: '100%', display: 'flex', flexDirection: 'column' }}>
<CardActionArea sx={{ flexGrow: 1, alignItems: 'stretch', display: 'flex', flexDirection: 'column' }}>
<CardMedia
component="img"
height="140"
@@ -91,8 +93,8 @@ const Projects = () => {
alt={project.name}
onClick={() => handleLinkClick(process.env.PUBLIC_URL + "Projects/" + project.img)}
/>
<CardContent align="left">
<Typography gutterBottom component="div">
<CardContent align="left" sx={{ flexGrow: 1 }}>
<Typography gutterBottom component="div" variant="h6">
{project.name}
</Typography>
<List>
@@ -102,11 +104,11 @@ const Projects = () => {
</ListItem>
)}
</List>
<Box>
<Stack direction="row" flexWrap="wrap" gap={1}>
{project.tools.map((tool, index) =>
<Chip key={index} label={tool} />
<Chip key={index} label={tool} size="small" />
)}
</Box>
</Stack>
</CardContent>
</CardActionArea>
<CardActions>
@@ -132,7 +134,7 @@ const Projects = () => {
pagination={{
dynamicBullets: true,
}}
navigation={false}
navigation={width >= 480}
speed={400}
spaceBetween={10}
slidesPerView={width >= 480 ? 2.6 : 1.1}
@@ -142,7 +144,7 @@ const Projects = () => {
{projectsData.map((project) =>
<SwiperSlide sx={{
width: 'auto',
flexShrink: 0, height: '90%'
flexShrink: 0, height: 'auto'
}} key={project.key}>
<ProjectCard project={project} />
</SwiperSlide>

43
src/theme.js Normal file
View File

@@ -0,0 +1,43 @@
import { createTheme } from '@mui/material/styles';
// Central place to tweak the site's look and feel.
export const getAppTheme = (mode) => createTheme({
palette: {
mode,
primary: {
main: mode === 'dark' ? '#7ad0c9' : '#00695f',
},
secondary: {
main: '#ef6c00',
},
background: mode === 'dark'
? { default: '#121212', paper: '#1c1c1c' }
: { default: '#f7f7f5', paper: '#ffffff' },
},
shape: {
borderRadius: 10,
},
typography: {
fontFamily: [
'-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', '"Fira Sans"', '"Droid Sans"', '"Helvetica Neue"',
'sans-serif',
].join(','),
h5: {
fontWeight: 600,
},
},
components: {
MuiCard: {
styleOverrides: {
root: {
transition: 'transform 200ms ease, box-shadow 200ms ease',
'&:hover': {
transform: 'translateY(-4px)',
boxShadow: '0 8px 20px rgba(0,0,0,0.15)',
},
},
},
},
},
});