mirror of
https://github.com/20kaushik02/portfolio-website.git
synced 2026-09-10 05:54:08 +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>
|
||||
<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>
|
||||
|
||||
45
src/App.js
45
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 { 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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
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