mirror of
https://github.com/hazemKrimi/personal-website.git
synced 2026-05-01 18:00:26 +00:00
Update project structure
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { FC, useContext } from 'react';
|
import { FC, useContext } from 'react';
|
||||||
import { DarkModeContext } from '../../components/DarkMode';
|
import { ThemeContext } from '../../styles/theme';
|
||||||
import { Props } from './types';
|
import { Props } from './types';
|
||||||
import { Btn } from './styles';
|
import { Btn } from './styles';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
@@ -12,18 +12,9 @@ const Button: FC<Props & { className?: string }> = ({
|
|||||||
children,
|
children,
|
||||||
className
|
className
|
||||||
}) => {
|
}) => {
|
||||||
const { dark } = useContext(DarkModeContext);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={href} passHref>
|
<Link href={href} passHref>
|
||||||
<Btn
|
<Btn as='a' target={target} variant={variant} onClick={onClick} className={className}>
|
||||||
as='a'
|
|
||||||
target={target}
|
|
||||||
variant={variant}
|
|
||||||
dark={dark}
|
|
||||||
onClick={onClick}
|
|
||||||
className={className}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</Btn>
|
</Btn>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -3,5 +3,4 @@ export type Props = {
|
|||||||
href: string;
|
href: string;
|
||||||
target?: HTMLAnchorElement['target'];
|
target?: HTMLAnchorElement['target'];
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
dark?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { FC, useContext } from 'react';
|
import { FC } from 'react';
|
||||||
import { DarkModeContext } from '../../components/DarkMode';
|
|
||||||
import { StyledCard } from './styles';
|
import { StyledCard } from './styles';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
@@ -15,11 +14,9 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Card: FC<Props> = ({ title, description, image, tags, href, target, onClick }) => {
|
const Card: FC<Props> = ({ title, description, image, tags, href, target, onClick }) => {
|
||||||
const { dark } = useContext(DarkModeContext);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={href} passHref>
|
<Link href={href} passHref>
|
||||||
<StyledCard as='a' target={target} dark={dark} onClick={onClick} image={!!image}>
|
<StyledCard as='a' target={target} onClick={onClick} image={!!image}>
|
||||||
<div>
|
<div>
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
<p>{description}</p>
|
<p>{description}</p>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const StyledCard = styled.div<{ dark: boolean; image: boolean }>`
|
export const StyledCard = styled.div<{ image: boolean }>`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
@@ -1,44 +1,44 @@
|
|||||||
import { FC, useContext } from 'react';
|
import { FC, useContext } from 'react';
|
||||||
import { DarkModeContext } from '../../components/DarkMode';
|
import { ThemeContext } from '../../styles/theme';
|
||||||
import { StyledFooter } from './styles';
|
import { StyledFooter } from './styles';
|
||||||
import IconButton from '../../components/IconButton';
|
import IconButton from '../../components/IconButton';
|
||||||
|
|
||||||
const Footer: FC = () => {
|
const Footer: FC = () => {
|
||||||
const { dark } = useContext(DarkModeContext);
|
const { mode } = useContext(ThemeContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledFooter>
|
<StyledFooter>
|
||||||
<div className='contact'>
|
<div className='contact'>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={dark ? '/icons/light-github.svg' : '/icons/dark-github.svg'}
|
icon={mode === 'dark' ? '/icons/light-github.svg' : '/icons/dark-github.svg'}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
href='https://github.com/hazemKrimi'
|
href='https://github.com/hazemKrimi'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={dark ? '/icons/light-twitter.svg' : '/icons/dark-twitter.svg'}
|
icon={mode === 'dark' ? '/icons/light-twitter.svg' : '/icons/dark-twitter.svg'}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
href='https://twitter.com/HazemKrimi'
|
href='https://twitter.com/HazemKrimi'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={dark ? '/icons/light-linkedin.svg' : '/icons/dark-linkedin.svg'}
|
icon={mode === 'dark' ? '/icons/light-linkedin.svg' : '/icons/dark-linkedin.svg'}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
href='https://linkedin.com/in/hazemkrimi'
|
href='https://linkedin.com/in/hazemkrimi'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={dark ? '/icons/light-codepen.svg' : '/icons/dark-codepen.svg'}
|
icon={mode === 'dark' ? '/icons/light-codepen.svg' : '/icons/dark-codepen.svg'}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
href='https://codepen.io/hazemkrimi'
|
href='https://codepen.io/hazemkrimi'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={dark ? '/icons/light-dribbble.svg' : '/icons/dark-dribbble.svg'}
|
icon={mode === 'dark' ? '/icons/light-dribbble.svg' : '/icons/dark-dribbble.svg'}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
href='https://dribbble.com/HazemKrimi'
|
href='https://dribbble.com/HazemKrimi'
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import { FC, useContext } from 'react';
|
|
||||||
import { createGlobalStyle } from 'styled-components';
|
import { createGlobalStyle } from 'styled-components';
|
||||||
import { DarkModeContext } from '../components/DarkMode';
|
|
||||||
interface Props {
|
|
||||||
dark: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Global = createGlobalStyle<Props>`
|
const GlobalStyles = createGlobalStyle`
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -71,10 +66,4 @@ const Global = createGlobalStyle<Props>`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const GlobalStyles: FC = () => {
|
|
||||||
const { dark } = useContext(DarkModeContext);
|
|
||||||
|
|
||||||
return <Global dark={dark} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GlobalStyles;
|
export default GlobalStyles;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { FC, useContext } from 'react';
|
import { FC, useContext } from 'react';
|
||||||
import { DarkModeContext } from '../../components/DarkMode';
|
import { ThemeContext } from '../../styles/theme';
|
||||||
import { Wrapper } from './styles';
|
import { Wrapper } from './styles';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
|
||||||
const Hero: FC = () => {
|
const Hero: FC = () => {
|
||||||
const { dark } = useContext(DarkModeContext);
|
const { mode } = useContext(ThemeContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
@@ -16,7 +16,7 @@ const Hero: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className='illustration'>
|
<div className='illustration'>
|
||||||
<Image
|
<Image
|
||||||
src={dark ? '/dark-illustration.svg' : '/light-illustration.svg'}
|
src={mode === 'dark' ? '/dark-illustration.svg' : '/light-illustration.svg'}
|
||||||
width='100%'
|
width='100%'
|
||||||
height='100%'
|
height='100%'
|
||||||
layout='responsive'
|
layout='responsive'
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { FC, useContext } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { DarkModeContext } from '../components/DarkMode';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -12,14 +11,14 @@ interface Props {
|
|||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
|
onChange?: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SmallField = styled.input<{ dark: boolean }>`
|
const SmallField = styled.input`
|
||||||
border: none;
|
border: none;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background: var(--secondary-background);
|
background: var(--secondary-background);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const BigField = styled.textarea<{ dark: boolean }>`
|
const BigField = styled.textarea`
|
||||||
resize: none;
|
resize: none;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
@@ -37,11 +36,8 @@ const Input: FC<Props & { className?: string }> = ({
|
|||||||
className,
|
className,
|
||||||
onChange
|
onChange
|
||||||
}) => {
|
}) => {
|
||||||
const { dark } = useContext(DarkModeContext);
|
|
||||||
|
|
||||||
return variant === 'small' ? (
|
return variant === 'small' ? (
|
||||||
<SmallField
|
<SmallField
|
||||||
dark={dark}
|
|
||||||
type={type}
|
type={type}
|
||||||
name={name}
|
name={name}
|
||||||
value={value}
|
value={value}
|
||||||
@@ -52,7 +48,6 @@ const Input: FC<Props & { className?: string }> = ({
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<BigField
|
<BigField
|
||||||
dark={dark}
|
|
||||||
name={name}
|
name={name}
|
||||||
value={value}
|
value={value}
|
||||||
required={required}
|
required={required}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { FC, useContext } from 'react';
|
import { FC, useContext } from 'react';
|
||||||
import { DarkModeContext } from '../../components/DarkMode';
|
import { ThemeContext } from '../../styles/theme';
|
||||||
import { Props } from './types';
|
import { Props } from './types';
|
||||||
import { Btn } from './styles';
|
import { Btn } from './styles';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
@@ -13,7 +13,7 @@ const MDXButton: FC<Props & { className?: string }> = ({
|
|||||||
disabled,
|
disabled,
|
||||||
className
|
className
|
||||||
}) => {
|
}) => {
|
||||||
const { dark } = useContext(DarkModeContext);
|
const { mode } = useContext(ThemeContext);
|
||||||
|
|
||||||
return link ? (
|
return link ? (
|
||||||
<Link href={link} passHref>
|
<Link href={link} passHref>
|
||||||
@@ -22,7 +22,7 @@ const MDXButton: FC<Props & { className?: string }> = ({
|
|||||||
target={target}
|
target={target}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
type={type}
|
type={type}
|
||||||
dark={dark}
|
mode={mode}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={className}
|
className={className}
|
||||||
>
|
>
|
||||||
@@ -30,7 +30,7 @@ const MDXButton: FC<Props & { className?: string }> = ({
|
|||||||
</Btn>
|
</Btn>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<Btn variant={variant} type={type} dark={dark} disabled={disabled} className={className}>
|
<Btn variant={variant} type={type} mode={mode} disabled={disabled} className={className}>
|
||||||
{children}
|
{children}
|
||||||
</Btn>
|
</Btn>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ export const Btn = styled.button<Props>`
|
|||||||
['action', 'outline'].includes(variant as string) ? 'block' : 'inline'};
|
['action', 'outline'].includes(variant as string) ? 'block' : 'inline'};
|
||||||
width: ${({ variant }) => (['action', 'outline'].includes(variant as string) ? '100%' : 'auto')};
|
width: ${({ variant }) => (['action', 'outline'].includes(variant as string) ? '100%' : 'auto')};
|
||||||
/* TODO: fix theme blue color problem */
|
/* TODO: fix theme blue color problem */
|
||||||
background: ${({ variant, theme }) => (variant === 'action' ? '#1573CA' : 'none')};
|
background: ${({ variant }) => (variant === 'action' ? '#1573CA' : 'none')};
|
||||||
color: ${({ variant, dark }) => (variant === 'action' ? 'white' : dark ? 'white' : 'black')};
|
color: ${({ variant, mode }) =>
|
||||||
border: ${({ variant, dark }) =>
|
variant === 'action' ? 'white' : mode === 'dark' ? 'white' : 'black'};
|
||||||
variant === 'outline' ? `2px solid ${dark ? 'white' : 'black'}` : 'none'};
|
border: ${({ variant, mode }) =>
|
||||||
|
variant === 'outline' ? `2px solid ${mode === 'dark' ? 'white' : 'black'}` : 'none'};
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: ${({ variant }) =>
|
font-size: ${({ variant }) =>
|
||||||
['action', 'outline'].includes(variant as string) ? '1.05rem' : 'inherit'};
|
['action', 'outline'].includes(variant as string) ? '1.05rem' : 'inherit'};
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ export type Props = {
|
|||||||
type?: 'button' | 'submit';
|
type?: 'button' | 'submit';
|
||||||
link?: string;
|
link?: string;
|
||||||
target?: HTMLAnchorElement['target'];
|
target?: HTMLAnchorElement['target'];
|
||||||
dark?: boolean;
|
mode?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { FC, useContext, useRef, useEffect } from 'react';
|
import { FC, useContext, useRef, useEffect } from 'react';
|
||||||
import { DarkModeContext } from '../DarkMode';
|
import { ThemeContext } from '../../styles/theme';
|
||||||
import { Props } from './types';
|
import { Props } from './types';
|
||||||
import { Bar } from './styles';
|
import { Bar } from './styles';
|
||||||
import IconButton from '../IconButton';
|
import IconButton from '../IconButton';
|
||||||
import Button from '../Button';
|
import Button from '../Button';
|
||||||
|
|
||||||
const MobileNav: FC<Props> = ({ open, close }) => {
|
const MobileNav: FC<Props> = ({ open, close }) => {
|
||||||
const { dark, toggle } = useContext(DarkModeContext);
|
const { mode, toggle } = useContext(ThemeContext);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -29,16 +29,16 @@ const MobileNav: FC<Props> = ({ open, close }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Bar dark={dark} open={open} ref={ref}>
|
<Bar open={open} ref={ref}>
|
||||||
<div className='close'>
|
<div className='close'>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={dark ? '/icons/dark-close.svg' : '/icons/light-close.svg'}
|
icon={mode === 'dark' ? '/icons/dark-close.svg' : '/icons/light-close.svg'}
|
||||||
onClick={close}
|
onClick={close}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='mobile-button-wrapper'>
|
<div className='mobile-button-wrapper'>
|
||||||
<Button href='#' onClick={() => toggle()}>
|
<Button href='#' onClick={() => toggle()}>
|
||||||
{dark ? 'Light Mode' : 'Dark Mode'}
|
{mode === 'dark' ? 'Light Mode' : 'Dark Mode'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className='mobile-button-wrapper'>
|
<div className='mobile-button-wrapper'>
|
||||||
|
|||||||
@@ -4,6 +4,5 @@ export type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type StyledProps = {
|
export type StyledProps = {
|
||||||
dark: boolean;
|
|
||||||
open: boolean;
|
open: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { FC, useContext, useState } from 'react';
|
import { FC, useContext, useState } from 'react';
|
||||||
import { DarkModeContext } from '../DarkMode';
|
import { ThemeContext } from '../../styles/theme';
|
||||||
import { Bar } from './styles';
|
import { Bar } from './styles';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
@@ -9,7 +9,7 @@ import MobileNav from '../MobileNav';
|
|||||||
|
|
||||||
const Nav: FC = () => {
|
const Nav: FC = () => {
|
||||||
const [mobileNavOpen, setMobileNavOpen] = useState<boolean>(false);
|
const [mobileNavOpen, setMobileNavOpen] = useState<boolean>(false);
|
||||||
const { dark, toggle } = useContext(DarkModeContext);
|
const { mode, toggle } = useContext(ThemeContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Bar>
|
<Bar>
|
||||||
@@ -17,7 +17,7 @@ const Nav: FC = () => {
|
|||||||
<a className='logo'>
|
<a className='logo'>
|
||||||
<Image
|
<Image
|
||||||
className='logo-image'
|
className='logo-image'
|
||||||
src={dark ? '/light-logo.svg' : '/dark-logo.svg'}
|
src={mode === 'dark' ? '/light-logo.svg' : '/dark-logo.svg'}
|
||||||
alt='Logo Image'
|
alt='Logo Image'
|
||||||
width={48}
|
width={48}
|
||||||
height={48}
|
height={48}
|
||||||
@@ -26,7 +26,10 @@ const Nav: FC = () => {
|
|||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<div className='buttons'>
|
<div className='buttons'>
|
||||||
<IconButton icon={dark ? '/icons/sun.svg' : '/icons/moon.svg'} onClick={toggle} />
|
<IconButton
|
||||||
|
icon={mode === 'dark' ? '/icons/sun.svg' : '/icons/moon.svg'}
|
||||||
|
onClick={toggle}
|
||||||
|
/>
|
||||||
<Button href='/about'>About</Button>
|
<Button href='/about'>About</Button>
|
||||||
<Button href='/portfolio'>Portfolio</Button>
|
<Button href='/portfolio'>Portfolio</Button>
|
||||||
<Button href='/blog'>Blog</Button>
|
<Button href='/blog'>Blog</Button>
|
||||||
@@ -39,7 +42,7 @@ const Nav: FC = () => {
|
|||||||
Resume
|
Resume
|
||||||
</Button>
|
</Button>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={dark ? '/icons/light-menu.svg' : '/icons/dark-menu.svg'}
|
icon={mode === 'dark' ? '/icons/light-menu.svg' : '/icons/dark-menu.svg'}
|
||||||
onClick={() => setMobileNavOpen(true)}
|
onClick={() => setMobileNavOpen(true)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+6
-6
@@ -5,10 +5,10 @@ import Head from 'next/head';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
import Nav from '../components/Nav';
|
import Nav from '../components/Nav';
|
||||||
import DarkMode from '../components/DarkMode';
|
import Theme from '../styles/theme';
|
||||||
import Container from '../components/Container';
|
import Container from '../components/Container';
|
||||||
import GlobalStyles from '../components/GlobalStyles';
|
import GlobalStyles from '../components/GlobalStyles';
|
||||||
import Theme from '../components/Theme';
|
import SharedStyles from '../styles/shared';
|
||||||
import Footer from '../components/Footer';
|
import Footer from '../components/Footer';
|
||||||
|
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
@@ -42,16 +42,16 @@ const App = ({ Component, pageProps }: AppProps) => {
|
|||||||
rel='stylesheet'
|
rel='stylesheet'
|
||||||
/>
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
<DarkMode>
|
<Theme>
|
||||||
<Theme>
|
<SharedStyles>
|
||||||
<GlobalStyles />
|
<GlobalStyles />
|
||||||
<Container>
|
<Container>
|
||||||
<Nav />
|
<Nav />
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
<Footer />
|
<Footer />
|
||||||
</Container>
|
</Container>
|
||||||
</Theme>
|
</SharedStyles>
|
||||||
</DarkMode>
|
</Theme>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+9
-9
@@ -48,8 +48,8 @@ class Doc extends Document {
|
|||||||
<script
|
<script
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: `
|
__html: `
|
||||||
function getInitialTheme() {
|
function getInitialThemeMode() {
|
||||||
const persistedColorPreference = window.localStorage.getItem('theme');
|
const persistedColorPreference = window.localStorage.getItem('mode');
|
||||||
const hasPersistedPreference = typeof persistedColorPreference === 'string';
|
const hasPersistedPreference = typeof persistedColorPreference === 'string';
|
||||||
|
|
||||||
if (hasPersistedPreference) {
|
if (hasPersistedPreference) {
|
||||||
@@ -67,29 +67,29 @@ class Doc extends Document {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
const theme = getInitialTheme();
|
const mode = getInitialThemeMode();
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
const lightFavicon = document.querySelector('link#light-favicon');
|
const lightFavicon = document.querySelector('link#light-favicon');
|
||||||
const darkFavicon = document.querySelector('link#dark-favicon');
|
const darkFavicon = document.querySelector('link#dark-favicon');
|
||||||
|
|
||||||
root.style.setProperty('--theme', theme);
|
root.style.setProperty('--mode', mode);
|
||||||
root.style.setProperty(
|
root.style.setProperty(
|
||||||
'--background',
|
'--background',
|
||||||
theme === 'light' ? '#F9F9F9' : '#262626'
|
mode === 'light' ? '#F9F9F9' : '#262626'
|
||||||
);
|
);
|
||||||
root.style.setProperty(
|
root.style.setProperty(
|
||||||
'--secondary-background',
|
'--secondary-background',
|
||||||
theme === 'light' ? 'white' : '#2F2F2F'
|
mode === 'light' ? 'white' : '#2F2F2F'
|
||||||
);
|
);
|
||||||
root.style.setProperty(
|
root.style.setProperty(
|
||||||
'--text',
|
'--text',
|
||||||
theme === 'light' ? 'black' : 'white'
|
mode === 'light' ? 'black' : 'white'
|
||||||
);
|
);
|
||||||
root.style.setProperty(
|
root.style.setProperty(
|
||||||
'--text-inverted',
|
'--text-inverted',
|
||||||
theme === 'light' ? 'white' : 'black'
|
mode === 'light' ? 'white' : 'black'
|
||||||
);
|
);
|
||||||
document.head.append(theme === 'light' ? darkFavicon : lightFavicon);
|
document.head.append(mode === 'light' ? darkFavicon : lightFavicon);
|
||||||
})();
|
})();
|
||||||
`
|
`
|
||||||
}}
|
}}
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
import React, { FC, useContext, useState } from 'react';
|
import React, { FC, useContext, useState } from 'react';
|
||||||
import { DarkModeContext } from '../components/DarkMode';
|
import { ThemeContext } from '../styles/theme';
|
||||||
import { useForm, ValidationError } from '@formspree/react';
|
import { useForm, ValidationError } from '@formspree/react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { Wrapper } from '../styles/about';
|
import { Wrapper } from '../styles/about';
|
||||||
@@ -8,7 +8,7 @@ import Input from '../components/Input';
|
|||||||
import MDXButton from '../components/MDXButton';
|
import MDXButton from '../components/MDXButton';
|
||||||
|
|
||||||
const About: FC = () => {
|
const About: FC = () => {
|
||||||
const { dark } = useContext(DarkModeContext);
|
const { mode } = useContext(ThemeContext);
|
||||||
const [form, setForm] = useState<{ name: string; email: string; message: string }>({
|
const [form, setForm] = useState<{ name: string; email: string; message: string }>({
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
@@ -53,7 +53,7 @@ const About: FC = () => {
|
|||||||
/>
|
/>
|
||||||
<title>About | Hazem Krimi</title>
|
<title>About | Hazem Krimi</title>
|
||||||
</Head>
|
</Head>
|
||||||
<Wrapper dark={dark}>
|
<Wrapper>
|
||||||
<div className='content'>
|
<div className='content'>
|
||||||
<div>
|
<div>
|
||||||
<h1>About Me</h1>
|
<h1>About Me</h1>
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const Wrapper = styled.div<{ dark: boolean }>`
|
export const Wrapper = styled.div`
|
||||||
padding: 1rem 0rem;
|
padding: 1rem 0rem;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { ThemeProvider, DefaultTheme } from 'styled-components';
|
import { ThemeProvider, DefaultTheme } from 'styled-components';
|
||||||
|
|
||||||
const Theme: FC = ({ children }) => {
|
const Shared: FC = ({ children }) => {
|
||||||
const theme: DefaultTheme = {
|
const theme: DefaultTheme = {
|
||||||
colors: {
|
colors: {
|
||||||
dark: {
|
dark: {
|
||||||
@@ -19,4 +19,4 @@ const Theme: FC = ({ children }) => {
|
|||||||
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
|
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Theme;
|
export default Shared;
|
||||||
@@ -1,39 +1,39 @@
|
|||||||
import { FC, useReducer, useEffect, createContext } from 'react';
|
import { FC, useReducer, useEffect, createContext } from 'react';
|
||||||
|
|
||||||
export const DarkModeContext = createContext<{ dark: boolean; toggle: () => void }>({
|
export const ThemeContext = createContext<{ mode: string; toggle: () => void }>({
|
||||||
dark: false,
|
mode: 'light',
|
||||||
toggle: () => {}
|
toggle: () => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
const reducer = (state: boolean, action: { type: string }) => {
|
const reducer = (state: string, action: { type: string }) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'TOGGLE':
|
case 'TOGGLE':
|
||||||
return !state;
|
return state === 'light' ? 'dark' : 'light';
|
||||||
case 'DARK':
|
case 'DARK':
|
||||||
return true;
|
return 'dark';
|
||||||
case 'LIGHT':
|
case 'LIGHT':
|
||||||
return false;
|
return 'light';
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const DarkMode: FC = ({ children }) => {
|
const Theme: FC = ({ children }) => {
|
||||||
const [dark, dispatch] = useReducer(reducer, false);
|
const [mode, dispatch] = useReducer(reducer, 'light');
|
||||||
const toggle = () => {
|
const toggle = () => {
|
||||||
const root = window.document.documentElement;
|
const root = window.document.documentElement;
|
||||||
const lightFavicon = document.querySelector('link#light-favicon')!;
|
const lightFavicon = document.querySelector('link#light-favicon')!;
|
||||||
const darkFavicon = document.querySelector('link#dark-favicon')!;
|
const darkFavicon = document.querySelector('link#dark-favicon')!;
|
||||||
|
|
||||||
if (dark) {
|
if (mode === 'dark') {
|
||||||
window.localStorage.setItem('theme', 'light');
|
window.localStorage.setItem('mode', 'light');
|
||||||
root.style.setProperty('--background', '#F9F9F9');
|
root.style.setProperty('--background', '#F9F9F9');
|
||||||
root.style.setProperty('--secondary-background', 'white');
|
root.style.setProperty('--secondary-background', 'white');
|
||||||
root.style.setProperty('--text', 'black');
|
root.style.setProperty('--text', 'black');
|
||||||
root.style.setProperty('--text-inverted', 'white');
|
root.style.setProperty('--text-inverted', 'white');
|
||||||
document.head.append(darkFavicon);
|
document.head.append(darkFavicon);
|
||||||
} else {
|
} else {
|
||||||
window.localStorage.setItem('theme', 'dark');
|
window.localStorage.setItem('mode', 'dark');
|
||||||
root.style.setProperty('--background', '#262626');
|
root.style.setProperty('--background', '#262626');
|
||||||
root.style.setProperty('--secondary-background', '#2F2F2F');
|
root.style.setProperty('--secondary-background', '#2F2F2F');
|
||||||
root.style.setProperty('--text', 'white');
|
root.style.setProperty('--text', 'white');
|
||||||
@@ -46,12 +46,12 @@ const DarkMode: FC = ({ children }) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const root = window.document.documentElement;
|
const root = window.document.documentElement;
|
||||||
const initialTheme = root.style.getPropertyValue('--theme');
|
const initialThemeMode = root.style.getPropertyValue('--mode');
|
||||||
|
|
||||||
dispatch({ type: initialTheme === 'dark' ? 'DARK' : 'LIGHT' });
|
dispatch({ type: initialThemeMode === 'dark' ? 'DARK' : 'LIGHT' });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <DarkModeContext.Provider value={{ dark, toggle }}>{children}</DarkModeContext.Provider>;
|
return <ThemeContext.Provider value={{ mode, toggle }}>{children}</ThemeContext.Provider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DarkMode;
|
export default Theme;
|
||||||
Reference in New Issue
Block a user