1
1
mirror of https://github.com/theoludwig/theoludwig.git synced 2025-05-29 22:37:44 +02:00

feat: rewrite to Next.js v13 app directory

Improvements:
- Hide switch theme input (ugly little white square)
- i18n without subpath (e.g: /fr or /en), same url whatever the locale used
This commit is contained in:
2023-07-31 19:06:46 +02:00
parent 5640f1b434
commit 6b29ce9b15
61 changed files with 755 additions and 787 deletions

21
app/error.tsx Normal file
View File

@ -0,0 +1,21 @@
'use client'
import { useEffect } from 'react'
import { ErrorPage } from '@/components/ErrorPage'
export interface ErrorHandlingProps {
error: Error
}
const ErrorHandling = (props: ErrorHandlingProps): JSX.Element => {
const { error } = props
useEffect(() => {
console.error(error)
}, [error])
return <ErrorPage statusCode={500} message='Server error' />
}
export default ErrorHandling

69
app/globals.css Normal file
View File

@ -0,0 +1,69 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.break-wrap-words {
word-wrap: break-word;
word-break: break-word;
}
.prose {
@apply !max-w-5xl scroll-smooth text-gray dark:text-gray-300;
}
.prose p {
@apply text-justify;
}
.prose [id]::before {
content: '';
display: block;
height: 90px;
margin-top: -90px;
visibility: hidden;
}
.prose a,
.prose strong {
@apply text-yellow dark:text-yellow-dark;
}
.prose h2,
.prose h3,
.prose h4,
.prose h5,
.prose h6 {
@apply mt-1 text-gray dark:text-gray-dark;
}
.prose code {
color: #ce9178;
}
.prose :where(code):not(:where([class~='not-prose'] *))::before,
.prose :where(code):not(:where([class~='not-prose'] *))::after {
content: '';
}
.shiki {
white-space: pre-wrap !important;
}
code {
counter-reset: step;
counter-increment: step 0;
}
code .line::before {
content: counter(step);
counter-increment: step;
width: 1rem;
margin-right: 1.5rem;
display: inline-block;
text-align: right;
color: rgba(133, 133, 133, 0.8);
word-wrap: normal;
word-break: normal;
}
.katex .base {
display: inline !important;
white-space: normal !important;
width: 100% !important;
}

71
app/layout.tsx Normal file
View File

@ -0,0 +1,71 @@
import type { Metadata } from 'next'
import '@fontsource/montserrat/400.css'
import '@fontsource/montserrat/600.css'
import './globals.css'
import { Providers } from '@/components/Providers'
import { Header } from '@/components/Header'
import { Footer } from '@/components/Footer'
import { getI18n } from '@/i18n/i18n.server'
const title = 'Théo LUDWIG'
const description =
'Théo LUDWIG - Developer Full Stack • Open-Source enthusiast'
const image = '/images/icon-96x96.png'
const url = new URL('https://theoludwig.fr')
const locale = 'fr-FR, en-US'
export const metadata: Metadata = {
title,
description,
metadataBase: url,
openGraph: {
title,
description,
url,
siteName: title,
images: [
{
url: image,
width: 96,
height: 96
}
],
locale,
type: 'website'
},
icons: {
icon: '/images/icon-96x96.png'
},
twitter: {
card: 'summary',
title,
description,
images: [image]
}
}
interface RootLayoutProps {
children: React.ReactNode
}
const RootLayout = (props: RootLayoutProps): JSX.Element => {
const { children } = props
const i18n = getI18n()
return (
<html suppressHydrationWarning lang={i18n.locale}>
<body className='bg-white font-headline text-black dark:bg-black dark:text-white'>
<Providers>
<Header showLocale />
{children}
<Footer />
</Providers>
</body>
</html>
)
}
export default RootLayout

11
app/loading.tsx Normal file
View File

@ -0,0 +1,11 @@
import { Loader } from '@/components/Loader/Loader'
const Loading = (): JSX.Element => {
return (
<main className='flex flex-col flex-1 items-center justify-center'>
<Loader />
</main>
)
}
export default Loading

12
app/not-found.tsx Normal file
View File

@ -0,0 +1,12 @@
import { getI18n } from '@/i18n/i18n.server'
import { ErrorPage } from '@/components/ErrorPage'
const NotFound = (): JSX.Element => {
const i18n = getI18n()
return (
<ErrorPage statusCode={404} message={i18n.translate('errors.not-found')} />
)
}
export default NotFound

59
app/page.tsx Normal file
View File

@ -0,0 +1,59 @@
import { RevealFade } from '@/components/design/RevealFade'
import { Section } from '@/components/design/Section'
import { Interests } from '@/components/Interests'
import { Portfolio } from '@/components/Portfolio'
import { Profile } from '@/components/Profile'
import { SocialMediaList } from '@/components/Profile/SocialMediaList'
import { Skills } from '@/components/Skills'
import { OpenSource } from '@/components/OpenSource'
import { getI18n } from '@/i18n/i18n.server'
const HomePage = (): JSX.Element => {
const i18n = getI18n()
return (
<main className='flex flex-col md:mx-auto md:max-w-4xl lg:max-w-7xl'>
<Section isMain id='about'>
<Profile />
<SocialMediaList />
</Section>
<RevealFade>
<Section
id='interests'
heading={i18n.translate('home.interests.title')}
>
<Interests />
</Section>
</RevealFade>
<RevealFade>
<Section
id='skills'
heading={i18n.translate('home.skills.title')}
withoutShadowContainer
>
<Skills />
</Section>
</RevealFade>
<RevealFade>
<Section
id='portfolio'
heading={i18n.translate('home.portfolio.title')}
withoutShadowContainer
>
<Portfolio />
</Section>
</RevealFade>
<RevealFade>
<Section id='open-source' heading='Open source' withoutShadowContainer>
<OpenSource />
</Section>
</RevealFade>
</main>
)
}
export default HomePage