mirror of
https://github.com/20kaushik02/portfolio-website.git
synced 2026-09-10 14:04:09 +00:00
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:
committed by
GitHub
parent
de42149c8c
commit
6717cbb30e
@@ -3,8 +3,17 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<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 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
|
<link
|
||||||
rel="apple-touch-icon"
|
rel="apple-touch-icon"
|
||||||
sizes="180x180"
|
sizes="180x180"
|
||||||
@@ -29,7 +38,6 @@
|
|||||||
color="#5bbad5"
|
color="#5bbad5"
|
||||||
/>
|
/>
|
||||||
<meta name="msapplication-TileColor" content="#00a300" />
|
<meta name="msapplication-TileColor" content="#00a300" />
|
||||||
<meta name="theme-color" content="#ffffff" />
|
|
||||||
<title>Kaushik's Website</title>
|
<title>Kaushik's Website</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
39
src/App.js
39
src/App.js
@@ -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 Menu from './components/Menu';
|
||||||
|
import { getAppTheme } from './theme';
|
||||||
|
|
||||||
export const WidthContext = createContext();
|
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() {
|
function App() {
|
||||||
const [width, setWidth] = useState(0);
|
const [width, setWidth] = useState(0);
|
||||||
|
const [mode, setMode] = useState(getInitialMode);
|
||||||
|
|
||||||
// Get window dimensions on resize
|
// Get window dimensions on resize
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.addEventListener("resize", updateWindowDimensions);
|
window.addEventListener("resize", updateWindowDimensions);
|
||||||
@@ -20,10 +38,29 @@ function App() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateWindowDimensions();
|
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 (
|
return (
|
||||||
|
<ColorModeContext.Provider value={colorMode}>
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
|
<CssBaseline />
|
||||||
<WidthContext.Provider value={width}>
|
<WidthContext.Provider value={width}>
|
||||||
<Menu />
|
<Menu />
|
||||||
</WidthContext.Provider>
|
</WidthContext.Provider>
|
||||||
|
</ThemeProvider>
|
||||||
|
</ColorModeContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ const socialsData = [
|
|||||||
const Landing = () => {
|
const Landing = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Typography variant="h4" p={2} align="left">
|
||||||
|
Hi, I'm Kaushik 👋
|
||||||
|
</Typography>
|
||||||
<List>
|
<List>
|
||||||
{/* summary */}
|
{/* summary */}
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={"Welcome to my (messy) homepage. I like data!\
|
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."} />
|
||||||
My passions lie in data engineering/analytics, distributed technologies,\
|
|
||||||
networks and security, web development, machine learning...\
|
|
||||||
you get the picture." } />
|
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{/* current endeavor */}
|
{/* current endeavor */}
|
||||||
<ListItem>
|
<ListItem>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import * as React from 'react';
|
|||||||
import AppBar from '@mui/material/AppBar';
|
import AppBar from '@mui/material/AppBar';
|
||||||
import Avatar from '@mui/material/Avatar';
|
import Avatar from '@mui/material/Avatar';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import CssBaseline from '@mui/material/CssBaseline';
|
|
||||||
import Divider from '@mui/material/Divider';
|
import Divider from '@mui/material/Divider';
|
||||||
import Drawer from '@mui/material/Drawer';
|
import Drawer from '@mui/material/Drawer';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
@@ -16,6 +15,7 @@ import MenuIcon from '@mui/icons-material/Menu';
|
|||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import Link from '@mui/material/Link';
|
import Link from '@mui/material/Link';
|
||||||
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
|
|
||||||
import LandingIcon from '@mui/icons-material/Home';
|
import LandingIcon from '@mui/icons-material/Home';
|
||||||
import ProjectsIcon from '@mui/icons-material/AccountTree';
|
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 InterestsIcon from '@mui/icons-material/Headphones';
|
||||||
import ArticleIcon from '@mui/icons-material/Article';
|
import ArticleIcon from '@mui/icons-material/Article';
|
||||||
import BlogIcon from '@mui/icons-material/RateReview';
|
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 Landing from './Landing';
|
||||||
// import LoremIpsum from './LoremIpsum';
|
// import LoremIpsum from './LoremIpsum';
|
||||||
@@ -30,6 +32,7 @@ import Projects from './Projects';
|
|||||||
import Career from './Career';
|
import Career from './Career';
|
||||||
import { handleLinkClick } from '../utils/linkClick';
|
import { handleLinkClick } from '../utils/linkClick';
|
||||||
import UnderConstruction from './UnderConstruction';
|
import UnderConstruction from './UnderConstruction';
|
||||||
|
import { ColorModeContext } from '../App';
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth = 240;
|
||||||
|
|
||||||
@@ -64,6 +67,11 @@ function Menu(props) {
|
|||||||
const [mobileOpen, setMobileOpen] = React.useState(false);
|
const [mobileOpen, setMobileOpen] = React.useState(false);
|
||||||
const [isClosing, setIsClosing] = React.useState(false);
|
const [isClosing, setIsClosing] = React.useState(false);
|
||||||
const [activeSection, setActiveSection] = React.useState(menuSections[0]);
|
const [activeSection, setActiveSection] = React.useState(menuSections[0]);
|
||||||
|
const { mode, toggleMode } = React.useContext(ColorModeContext);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
document.title = `Kaushik | ${activeSection.display_name}`;
|
||||||
|
}, [activeSection]);
|
||||||
|
|
||||||
const handleDrawerClose = () => {
|
const handleDrawerClose = () => {
|
||||||
setIsClosing(true);
|
setIsClosing(true);
|
||||||
@@ -130,7 +138,6 @@ function Menu(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex' }}>
|
<Box sx={{ display: 'flex' }}>
|
||||||
<CssBaseline />
|
|
||||||
<AppBar
|
<AppBar
|
||||||
position="fixed"
|
position="fixed"
|
||||||
sx={{
|
sx={{
|
||||||
@@ -148,9 +155,14 @@ function Menu(props) {
|
|||||||
>
|
>
|
||||||
<MenuIcon />
|
<MenuIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h6" noWrap component="div">
|
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||||
{activeSection.appbar_text}
|
{activeSection.appbar_text}
|
||||||
</Typography>
|
</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>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import React, { useContext } from 'react';
|
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, SwiperSlide } from 'swiper/react';
|
||||||
import "swiper/css";
|
import "swiper/css";
|
||||||
|
import "swiper/css/pagination";
|
||||||
|
import "swiper/css/navigation";
|
||||||
import { Pagination, Navigation, EffectFade } from "swiper/modules";
|
import { Pagination, Navigation, EffectFade } from "swiper/modules";
|
||||||
|
|
||||||
import { WidthContext } from '../App';
|
import { WidthContext } from '../App';
|
||||||
@@ -82,8 +84,8 @@ const Projects = () => {
|
|||||||
const width = useContext(WidthContext);
|
const width = useContext(WidthContext);
|
||||||
const ProjectCard = ({ project }) => {
|
const ProjectCard = ({ project }) => {
|
||||||
return (
|
return (
|
||||||
<Card sx={{ maxWidth: 400 }}>
|
<Card sx={{ maxWidth: 400, height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||||
<CardActionArea>
|
<CardActionArea sx={{ flexGrow: 1, alignItems: 'stretch', display: 'flex', flexDirection: 'column' }}>
|
||||||
<CardMedia
|
<CardMedia
|
||||||
component="img"
|
component="img"
|
||||||
height="140"
|
height="140"
|
||||||
@@ -91,8 +93,8 @@ const Projects = () => {
|
|||||||
alt={project.name}
|
alt={project.name}
|
||||||
onClick={() => handleLinkClick(process.env.PUBLIC_URL + "Projects/" + project.img)}
|
onClick={() => handleLinkClick(process.env.PUBLIC_URL + "Projects/" + project.img)}
|
||||||
/>
|
/>
|
||||||
<CardContent align="left">
|
<CardContent align="left" sx={{ flexGrow: 1 }}>
|
||||||
<Typography gutterBottom component="div">
|
<Typography gutterBottom component="div" variant="h6">
|
||||||
{project.name}
|
{project.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
<List>
|
<List>
|
||||||
@@ -102,11 +104,11 @@ const Projects = () => {
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
)}
|
)}
|
||||||
</List>
|
</List>
|
||||||
<Box>
|
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||||
{project.tools.map((tool, index) =>
|
{project.tools.map((tool, index) =>
|
||||||
<Chip key={index} label={tool} />
|
<Chip key={index} label={tool} size="small" />
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Stack>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</CardActionArea>
|
</CardActionArea>
|
||||||
<CardActions>
|
<CardActions>
|
||||||
@@ -132,7 +134,7 @@ const Projects = () => {
|
|||||||
pagination={{
|
pagination={{
|
||||||
dynamicBullets: true,
|
dynamicBullets: true,
|
||||||
}}
|
}}
|
||||||
navigation={false}
|
navigation={width >= 480}
|
||||||
speed={400}
|
speed={400}
|
||||||
spaceBetween={10}
|
spaceBetween={10}
|
||||||
slidesPerView={width >= 480 ? 2.6 : 1.1}
|
slidesPerView={width >= 480 ? 2.6 : 1.1}
|
||||||
@@ -142,7 +144,7 @@ const Projects = () => {
|
|||||||
{projectsData.map((project) =>
|
{projectsData.map((project) =>
|
||||||
<SwiperSlide sx={{
|
<SwiperSlide sx={{
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
flexShrink: 0, height: '90%'
|
flexShrink: 0, height: 'auto'
|
||||||
}} key={project.key}>
|
}} key={project.key}>
|
||||||
<ProjectCard project={project} />
|
<ProjectCard project={project} />
|
||||||
</SwiperSlide>
|
</SwiperSlide>
|
||||||
|
|||||||
43
src/theme.js
Normal file
43
src/theme.js
Normal 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)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user