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

chore: better Prettier config for easier reviews

This commit is contained in:
2023-10-23 23:11:59 +02:00
parent c7ad15a465
commit e566ef6c38
105 changed files with 2138 additions and 2080 deletions

View File

@ -1,6 +1,6 @@
import Link from 'next/link'
import Link from "next/link"
import { getI18n } from '@/i18n/i18n.server'
import { getI18n } from "@/i18n/i18n.server"
export const FooterText = (): JSX.Element => {
const i18n = getI18n()
@ -8,12 +8,12 @@ export const FooterText = (): JSX.Element => {
return (
<p>
<Link
href='/'
className='text-yellow hover:underline dark:text-yellow-dark'
href="/"
className="text-yellow hover:underline dark:text-yellow-dark"
>
Théo LUDWIG
</Link>{' '}
| {i18n.translate('common.all-rights-reserved')}
</Link>{" "}
| {i18n.translate("common.all-rights-reserved")}
</p>
)
}

View File

@ -1,4 +1,4 @@
import { useMemo } from 'react'
import { useMemo } from "react"
interface FooterVersionProps {
version: string
@ -12,14 +12,14 @@ export const FooterVersion = (props: FooterVersionProps): JSX.Element => {
}, [version])
return (
<p className='mt-1'>
Version{' '}
<p className="mt-1">
Version{" "}
<a
data-cy='version-link'
className='text-yellow hover:underline dark:text-yellow-dark'
data-cy="version-link"
className="text-yellow hover:underline dark:text-yellow-dark"
href={versionLink}
target='_blank'
rel='noopener noreferrer'
target="_blank"
rel="noopener noreferrer"
>
{version}
</a>

View File

@ -1,12 +1,12 @@
import { FooterText } from './FooterText'
import { FooterVersion } from './FooterVersion'
import { FooterText } from "./FooterText"
import { FooterVersion } from "./FooterVersion"
export const Footer = async (): Promise<JSX.Element> => {
const { readPackage } = await import('read-pkg')
const { readPackage } = await import("read-pkg")
const { version } = await readPackage()
return (
<footer className='flex flex-col items-center justify-center border-t-2 border-gray-600 bg-white py-6 text-lg dark:border-gray-400 dark:bg-black'>
<footer className="flex flex-col items-center justify-center border-t-2 border-gray-600 bg-white py-6 text-lg dark:border-gray-400 dark:bg-black">
<FooterText />
<FooterVersion version={version} />
</footer>

View File

@ -1,15 +1,15 @@
export const Arrow = (): JSX.Element => {
return (
<svg
width='12'
height='8'
viewBox='0 0 12 8'
fill='none'
xmlns='http://www.w3.org/2000/svg'
width="12"
height="8"
viewBox="0 0 12 8"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
className='fill-current text-black dark:text-white'
d='M9.8024 0.292969L5.61855 4.58597L1.43469 0.292969L0.0566406 1.70697L5.61855 7.41397L11.1805 1.70697L9.8024 0.292969Z'
className="fill-current text-black dark:text-white"
d="M9.8024 0.292969L5.61855 4.58597L1.43469 0.292969L0.0566406 1.70697L5.61855 7.41397L11.1805 1.70697L9.8024 0.292969Z"
/>
</svg>
)

View File

@ -1,7 +1,7 @@
import Image from 'next/image'
import Image from "next/image"
import type { CookiesStore } from '@/utils/constants'
import { useI18n } from '@/i18n/i18n.client'
import type { CookiesStore } from "@/utils/constants"
import { useI18n } from "@/i18n/i18n.client"
export interface LocaleFlagProps {
locale: string
@ -22,7 +22,7 @@ export const LocaleFlag = (props: LocaleFlagProps): JSX.Element => {
src={`/images/locales/${locale}.svg`}
alt={locale}
/>
<p data-cy='locale-flag-text' className='mx-2 text-base'>
<p data-cy="locale-flag-text" className="mx-2 text-base">
{i18n.translate(`common.${locale}`)}
</p>
</>

View File

@ -1,14 +1,14 @@
'use client'
"use client"
import { usePathname } from 'next/navigation'
import { useCallback, useEffect, useState, useRef } from 'react'
import classNames from 'clsx'
import { usePathname } from "next/navigation"
import { useCallback, useEffect, useState, useRef } from "react"
import classNames from "clsx"
import type { Locale as LocaleType, CookiesStore } from '@/utils/constants'
import { LOCALES } from '@/utils/constants'
import type { Locale as LocaleType, CookiesStore } from "@/utils/constants"
import { LOCALES } from "@/utils/constants"
import { Arrow } from './Arrow'
import { LocaleFlag } from './LocaleFlag'
import { Arrow } from "./Arrow"
import { LocaleFlag } from "./LocaleFlag"
export interface LocalesProps {
currentLocale: string
@ -38,28 +38,28 @@ export const Locales = (props: LocalesProps): JSX.Element => {
}
}
window.document.addEventListener('click', handleClickEvent)
window.document.addEventListener("click", handleClickEvent)
return () => {
return window.removeEventListener('click', handleClickEvent)
return window.removeEventListener("click", handleClickEvent)
}
}, [])
const handleLocale = async (locale: LocaleType): Promise<void> => {
const { setLocale } = await import('@/i18n/i18n.server')
const { setLocale } = await import("@/i18n/i18n.server")
setLocale(locale)
}
if (pathname.startsWith('/blog')) {
if (pathname.startsWith("/blog")) {
return <></>
}
return (
<div className='flex cursor-pointer flex-col items-center justify-center'>
<div className="flex cursor-pointer flex-col items-center justify-center">
<div
ref={languageClickRef}
data-cy='locale-click'
className='mr-5 flex items-center'
data-cy="locale-click"
className="mr-5 flex items-center"
onClick={handleHiddenMenu}
>
<LocaleFlag
@ -70,10 +70,10 @@ export const Locales = (props: LocalesProps): JSX.Element => {
</div>
<ul
data-cy='locales-list'
data-cy="locales-list"
className={classNames(
'absolute top-14 z-10 mr-4 mt-3 flex w-32 list-none flex-col items-center justify-center rounded-lg bg-white p-0 shadow-lightFlag dark:bg-black dark:shadow-darkFlag',
{ hidden: hiddenMenu }
"absolute top-14 z-10 mr-4 mt-3 flex w-32 list-none flex-col items-center justify-center rounded-lg bg-white p-0 shadow-lightFlag dark:bg-black dark:shadow-darkFlag",
{ hidden: hiddenMenu },
)}
>
{LOCALES.filter((locale) => {
@ -82,7 +82,7 @@ export const Locales = (props: LocalesProps): JSX.Element => {
return (
<li
key={locale}
className='flex h-12 w-full items-center justify-center hover:bg-[#4f545c] hover:bg-opacity-20'
className="flex h-12 w-full items-center justify-center hover:bg-[#4f545c] hover:bg-opacity-20"
onClick={async () => {
return await handleLocale(locale)
}}

View File

@ -1,9 +1,9 @@
'use client'
"use client"
import classNames from 'clsx'
import classNames from "clsx"
import { useTheme } from '@/theme/theme.client'
import type { CookiesStore } from '@/utils/constants'
import { useTheme } from "@/theme/theme.client"
import type { CookiesStore } from "@/utils/constants"
export interface SwitchThemeProps {
cookiesStore: CookiesStore
@ -14,63 +14,63 @@ export const SwitchTheme = (props: SwitchThemeProps): JSX.Element => {
const theme = useTheme(cookiesStore)
const handleClick = async (): Promise<void> => {
const { setTheme } = await import('@/theme/theme.server')
const newTheme = theme === 'dark' ? 'light' : 'dark'
const { setTheme } = await import("@/theme/theme.server")
const newTheme = theme === "dark" ? "light" : "dark"
setTheme(newTheme)
}
return (
<div
className='flex items-center'
data-cy='switch-theme-click'
className="flex items-center"
data-cy="switch-theme-click"
onClick={handleClick}
>
<div className='relative inline-block cursor-pointer touch-pan-x select-none border-0 bg-transparent p-0'>
<div className='h-[24px] w-[50px] rounded-[30px] bg-[#4d4d4d] p-0 text-white transition-all duration-200 ease-in-out'>
<div className="relative inline-block cursor-pointer touch-pan-x select-none border-0 bg-transparent p-0">
<div className="h-[24px] w-[50px] rounded-[30px] bg-[#4d4d4d] p-0 text-white transition-all duration-200 ease-in-out">
<div
data-cy='switch-theme-dark'
data-cy="switch-theme-dark"
className={classNames(
'absolute bottom-0 left-[8px] top-0 mb-auto mt-auto h-[10px] w-[14px] leading-[0] transition-opacity duration-[250ms] ease-in-out',
"absolute bottom-0 left-[8px] top-0 mb-auto mt-auto h-[10px] w-[14px] leading-[0] transition-opacity duration-[250ms] ease-in-out",
{
'opacity-100': theme === 'dark',
'opacity-0': theme === 'light'
}
"opacity-100": theme === "dark",
"opacity-0": theme === "light",
},
)}
>
<span className='relative flex h-[10px] w-[10px] items-center justify-center'>
<span className="relative flex h-[10px] w-[10px] items-center justify-center">
🌜
</span>
</div>
<div
data-cy='switch-theme-light'
data-cy="switch-theme-light"
className={classNames(
'absolute bottom-0 right-[10px] top-0 mb-auto mt-auto h-[10px] w-[10px] leading-[0]',
"absolute bottom-0 right-[10px] top-0 mb-auto mt-auto h-[10px] w-[10px] leading-[0]",
{
'opacity-100': theme === 'light',
'opacity-0': theme === 'dark'
}
"opacity-100": theme === "light",
"opacity-0": theme === "dark",
},
)}
>
<span className='relative flex h-[10px] w-[10px] items-center justify-center'>
<span className="relative flex h-[10px] w-[10px] items-center justify-center">
🌞
</span>
</div>
</div>
<div
className={classNames(
'absolute top-[1px] box-border h-[22px] w-[22px] rounded-[50%] bg-[#fafafa] text-white transition-all duration-[250ms] ease-in-out',
"absolute top-[1px] box-border h-[22px] w-[22px] rounded-[50%] bg-[#fafafa] text-white transition-all duration-[250ms] ease-in-out",
{
'left-[27px]': theme === 'dark',
'left-0': theme === 'light'
}
"left-[27px]": theme === "dark",
"left-0": theme === "light",
},
)}
style={{ border: '1px solid #4d4d4d' }}
style={{ border: "1px solid #4d4d4d" }}
/>
<input
data-cy='switch-theme-input'
type='checkbox'
aria-label='Dark mode toggle'
className='absolute m-[-1px] h-[1px] w-[1px] overflow-hidden border-0 p-0 hidden'
data-cy="switch-theme-input"
type="checkbox"
aria-label="Dark mode toggle"
className="absolute m-[-1px] h-[1px] w-[1px] overflow-hidden border-0 p-0 hidden"
defaultChecked
/>
</div>

View File

@ -1,39 +1,39 @@
import { cookies } from 'next/headers'
import Link from 'next/link'
import Image from 'next/image'
import { cookies } from "next/headers"
import Link from "next/link"
import Image from "next/image"
import { getI18n } from '@/i18n/i18n.server'
import { getI18n } from "@/i18n/i18n.server"
import { Locales } from './Locales'
import { SwitchTheme } from './SwitchTheme'
import { Locales } from "./Locales"
import { SwitchTheme } from "./SwitchTheme"
export const Header = (): JSX.Element => {
const cookiesStore = cookies()
const i18n = getI18n()
return (
<header className='sticky top-0 z-50 flex w-full justify-between border-b-2 border-gray-600 bg-white px-6 py-2 dark:border-gray-400 dark:bg-black'>
<Link href='/'>
<div className='flex items-center justify-center'>
<header className="sticky top-0 z-50 flex w-full justify-between border-b-2 border-gray-600 bg-white px-6 py-2 dark:border-gray-400 dark:bg-black">
<Link href="/">
<div className="flex items-center justify-center">
<Image
quality={100}
width={60}
height={60}
src='/images/icon_small.png'
alt='Théo LUDWIG'
src="/images/icon_small.png"
alt="Théo LUDWIG"
priority
/>
<strong className='ml-1 hidden font-headline font-semibold text-yellow dark:text-yellow-dark xs:block'>
<strong className="ml-1 hidden font-headline font-semibold text-yellow dark:text-yellow-dark xs:block">
Théo LUDWIG
</strong>
</div>
</Link>
<div className='flex justify-between'>
<div className='flex flex-col items-center justify-center px-6'>
<div className="flex justify-between">
<div className="flex flex-col items-center justify-center px-6">
<Link
href='/blog'
data-cy='header-blog-link'
className='text-yellow hover:underline dark:text-yellow-dark'
href="/blog"
data-cy="header-blog-link"
className="text-yellow hover:underline dark:text-yellow-dark"
>
Blog
</Link>

View File

@ -1,4 +1,4 @@
import htmlParser from 'html-react-parser'
import htmlParser from "html-react-parser"
export interface InterestParagraphProps {
title: string
@ -6,14 +6,14 @@ export interface InterestParagraphProps {
}
export const InterestParagraph = (
props: InterestParagraphProps
props: InterestParagraphProps,
): JSX.Element => {
const { title, description } = props
return (
<>
<p className='my-6 text-center text-gray dark:text-gray-dark'>
<strong className='text-lg font-semibold text-yellow dark:text-yellow-dark'>
<p className="my-6 text-center text-gray dark:text-gray-dark">
<strong className="text-lg font-semibold text-yellow dark:text-yellow-dark">
{title}
</strong>
<br />

View File

@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import type { IconDefinition } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import type { IconDefinition } from "@fortawesome/free-solid-svg-icons"
interface InterestItemProps {
title: string
@ -10,9 +10,9 @@ export const InterestItem = (props: InterestItemProps): JSX.Element => {
const { fontAwesomeIcon, title } = props
return (
<li className='interest-item mx-2 my-2 h-8 w-8' title={title}>
<li className="interest-item mx-2 my-2 h-8 w-8" title={title}>
<FontAwesomeIcon
className='block h-full w-full text-yellow dark:text-yellow-dark'
className="block h-full w-full text-yellow dark:text-yellow-dark"
icon={fontAwesomeIcon}
/>
</li>

View File

@ -1,18 +1,18 @@
import { faCode, faMicrochip } from '@fortawesome/free-solid-svg-icons'
import { faGit } from '@fortawesome/free-brands-svg-icons'
import { faCode, faMicrochip } from "@fortawesome/free-solid-svg-icons"
import { faGit } from "@fortawesome/free-brands-svg-icons"
import { InterestItem } from './InterestItem'
import { InterestItem } from "./InterestItem"
export const InterestsList = (): JSX.Element => {
return (
<div className='my-4 flex justify-center'>
<ul className='m-0 flex w-96 list-none justify-around p-0'>
<InterestItem title='Developer Full Stack' fontAwesomeIcon={faCode} />
<div className="my-4 flex justify-center">
<ul className="m-0 flex w-96 list-none justify-around p-0">
<InterestItem title="Developer Full Stack" fontAwesomeIcon={faCode} />
<InterestItem
title='Passionate about High-Tech'
title="Passionate about High-Tech"
fontAwesomeIcon={faMicrochip}
/>
<InterestItem title='Open-Source enthusiast' fontAwesomeIcon={faGit} />
<InterestItem title="Open-Source enthusiast" fontAwesomeIcon={faGit} />
</ul>
</div>
)

View File

@ -1,21 +1,21 @@
import { getI18n } from '@/i18n/i18n.server'
import { getI18n } from "@/i18n/i18n.server"
import type { InterestParagraphProps } from './InterestParagraph'
import { InterestParagraph } from './InterestParagraph'
import { InterestsList } from './InterestsList'
import type { InterestParagraphProps } from "./InterestParagraph"
import { InterestParagraph } from "./InterestParagraph"
import { InterestsList } from "./InterestsList"
export const Interests = (): JSX.Element => {
const i18n = getI18n()
let paragraphs = i18n.translate<InterestParagraphProps[]>(
'home.interests.paragraphs'
"home.interests.paragraphs",
)
if (!Array.isArray(paragraphs)) {
paragraphs = []
}
return (
<div className='max-w-full'>
<div className="max-w-full">
{paragraphs.map((paragraph, index) => {
return <InterestParagraph key={index} {...paragraph} />
})}

View File

@ -1,5 +1,5 @@
import { ShadowContainer } from '@/components/design/ShadowContainer'
import { GitHubIcon } from '@/components/Profile/SocialMediaList/SocialMediaIcons/GitHubIcon'
import { ShadowContainer } from "@/components/design/ShadowContainer"
import { GitHubIcon } from "@/components/Profile/SocialMediaList/SocialMediaIcons/GitHubIcon"
export interface RepositoryProps {
name: string
@ -11,13 +11,13 @@ export const Repository = (props: RepositoryProps): JSX.Element => {
const { name, description, href } = props
return (
<ShadowContainer className='relative !mb-4 max-h-32 cursor-pointer p-6 transition-transform duration-200 ease-in-out hover:-translate-y-2'>
<a href={href} target='_blank' rel='noopener noreferrer'>
<div className='flex'>
<GitHubIcon className='mr-2 h-6' />
<span className='text-yellow dark:text-yellow-dark'>{name}</span>
<ShadowContainer className="relative !mb-4 max-h-32 cursor-pointer p-6 transition-transform duration-200 ease-in-out hover:-translate-y-2">
<a href={href} target="_blank" rel="noopener noreferrer">
<div className="flex">
<GitHubIcon className="mr-2 h-6" />
<span className="text-yellow dark:text-yellow-dark">{name}</span>
</div>
<p className='my-4'>{description}</p>
<p className="my-4">{description}</p>
</a>
</ShadowContainer>
)

View File

@ -1,35 +1,35 @@
import { getI18n } from '@/i18n/i18n.server'
import { getI18n } from "@/i18n/i18n.server"
import { Repository } from './Repository'
import { Repository } from "./Repository"
export const OpenSource = (): JSX.Element => {
const i18n = getI18n()
return (
<div className='mt-0 flex max-w-full flex-col items-center'>
<p className='text-center'>
{i18n.translate('home.open-source.description')}
<div className="mt-0 flex max-w-full flex-col items-center">
<p className="text-center">
{i18n.translate("home.open-source.description")}
</p>
<div className='my-6 grid grid-cols-1 gap-6 md:w-10/12 md:grid-cols-2'>
<div className="my-6 grid grid-cols-1 gap-6 md:w-10/12 md:grid-cols-2">
<Repository
name='nodejs/node'
description='Node.js JavaScript runtime ✨🐢🚀✨'
href='https://github.com/nodejs/node/commits?author=theoludwig'
name="nodejs/node"
description="Node.js JavaScript runtime ✨🐢🚀✨"
href="https://github.com/nodejs/node/commits?author=theoludwig"
/>
<Repository
name='standard/standard'
description='🌟 JavaScript Style Guide, with linter & automatic code fixer'
href='https://github.com/standard/standard/commits?author=theoludwig'
name="standard/standard"
description="🌟 JavaScript Style Guide, with linter & automatic code fixer"
href="https://github.com/standard/standard/commits?author=theoludwig"
/>
<Repository
name='nrwl/nx'
description='Smart, Fast and Extensible Build System'
href='https://github.com/nrwl/nx/commits?author=theoludwig'
name="nrwl/nx"
description="Smart, Fast and Extensible Build System"
href="https://github.com/nrwl/nx/commits?author=theoludwig"
/>
<Repository
name='vercel/next.js'
description='The React Framework'
href='https://github.com/vercel/next.js/commits?author=theoludwig'
name="vercel/next.js"
description="The React Framework"
href="https://github.com/vercel/next.js/commits?author=theoludwig"
/>
</div>
</div>

View File

@ -1,6 +1,6 @@
import Image from 'next/image'
import Image from "next/image"
import { ShadowContainer } from '@/components/design/ShadowContainer'
import { ShadowContainer } from "@/components/design/ShadowContainer"
export interface PortfolioItemProps {
title: string
@ -13,29 +13,29 @@ export const PortfolioItem = (props: PortfolioItemProps): JSX.Element => {
const { title, description, link, image } = props
return (
<ShadowContainer className='relative cursor-pointer items-center sm:ml-10'>
<ShadowContainer className="relative cursor-pointer items-center sm:ml-10">
<a
className='group inline-flex justify-center'
target='_blank'
rel='noopener noreferrer'
className="group inline-flex justify-center"
target="_blank"
rel="noopener noreferrer"
href={link}
aria-label={title}
>
<div className='flex justify-center'>
<div className="flex justify-center">
<Image
quality={100}
className='h-auto w-auto transition-opacity duration-500 group-hover:opacity-20 dark:group-hover:opacity-5'
className="h-auto w-auto transition-opacity duration-500 group-hover:opacity-20 dark:group-hover:opacity-5"
width={300}
height={300}
src={image}
alt={title}
/>
</div>
<div className='absolute bottom-0 h-auto overflow-hidden text-center opacity-0 transition-opacity duration-500 group-hover:opacity-100'>
<h3 className='my-6 text-xl font-semibold text-yellow dark:text-yellow-dark'>
<div className="absolute bottom-0 h-auto overflow-hidden text-center opacity-0 transition-opacity duration-500 group-hover:opacity-100">
<h3 className="my-6 text-xl font-semibold text-yellow dark:text-yellow-dark">
{title}
</h3>
<p className='my-6'>{description}</p>
<p className="my-6">{description}</p>
</div>
</a>
</ShadowContainer>

View File

@ -1,18 +1,18 @@
import { getI18n } from '@/i18n/i18n.server'
import { getI18n } from "@/i18n/i18n.server"
import type { PortfolioItemProps } from './PortfolioItem'
import { PortfolioItem } from './PortfolioItem'
import type { PortfolioItemProps } from "./PortfolioItem"
import { PortfolioItem } from "./PortfolioItem"
export const Portfolio = (): JSX.Element => {
const i18n = getI18n()
let items = i18n.translate<PortfolioItemProps[]>('home.portfolio.items')
let items = i18n.translate<PortfolioItemProps[]>("home.portfolio.items")
if (!Array.isArray(items)) {
items = []
}
return (
<div className='flex w-full flex-wrap justify-center px-3'>
<div className="flex w-full flex-wrap justify-center px-3">
{items.map((item, index) => {
return <PortfolioItem key={index} {...item} />
})}

View File

@ -1,18 +1,18 @@
import { getI18n } from '@/i18n/i18n.server'
import { getI18n } from "@/i18n/i18n.server"
export const ProfileDescriptionBottom = (): JSX.Element => {
const i18n = getI18n()
return (
<p className='mb-8 mt-8 text-base font-normal text-gray dark:text-gray-dark'>
{i18n.translate('home.about.description-bottom')}
{i18n.locale === 'fr-FR' ? (
<p className="mb-8 mt-8 text-base font-normal text-gray dark:text-gray-dark">
{i18n.translate("home.about.description-bottom")}
{i18n.locale === "fr-FR" ? (
<>
<br />
<br />
<a
href='/curriculum-vitae/index.html'
className='text-yellow hover:underline dark:text-yellow-dark'
href="/curriculum-vitae/index.html"
className="text-yellow hover:underline dark:text-yellow-dark"
>
Curriculum vitæ
</a>

View File

@ -1,15 +1,15 @@
import { getI18n } from '@/i18n/i18n.server'
import { getI18n } from "@/i18n/i18n.server"
export const ProfileInformation = (): JSX.Element => {
const i18n = getI18n()
return (
<div className='mb-6 border-b-2 border-gray-600 pb-2 font-headline dark:border-gray-400'>
<h1 className='mb-2 text-4xl font-semibold text-yellow dark:text-yellow-dark'>
<div className="mb-6 border-b-2 border-gray-600 pb-2 font-headline dark:border-gray-400">
<h1 className="mb-2 text-4xl font-semibold text-yellow dark:text-yellow-dark">
Théo LUDWIG
</h1>
<h2 className='mb-3 text-base'>
{i18n.translate('home.about.description')}
<h2 className="mb-3 text-base">
{i18n.translate("home.about.description")}
</h2>
</div>
)

View File

@ -8,14 +8,14 @@ export const ProfileItem = (props: ProfileItemProps): JSX.Element => {
const { title, value, link } = props
return (
<li className='mb-3 before:table after:clear-both after:table'>
<strong className='float-left block w-28 text-sm font-bold text-black dark:text-white'>
<li className="mb-3 before:table after:clear-both after:table">
<strong className="float-left block w-28 text-sm font-bold text-black dark:text-white">
{title}
</strong>
<span className='mb-4 ml-0 block text-sm font-normal text-gray dark:text-gray-dark sm:mb-0 sm:ml-32'>
<span className="mb-4 ml-0 block text-sm font-normal text-gray dark:text-gray-dark sm:mb-0 sm:ml-32">
{link != null ? (
<a
className='text-gray hover:underline dark:text-gray-dark'
className="text-gray hover:underline dark:text-gray-dark"
href={link}
>
{value}

View File

@ -1,12 +1,12 @@
'use client'
"use client"
import { useMemo } from 'react'
import { useMemo } from "react"
import { useI18n } from '@/i18n/i18n.client'
import { BIRTH_DATE, BIRTH_DATE_STRING, getAge } from '@/utils/getAge'
import type { CookiesStore } from '@/utils/constants'
import { useI18n } from "@/i18n/i18n.client"
import { BIRTH_DATE, BIRTH_DATE_STRING, getAge } from "@/utils/getAge"
import type { CookiesStore } from "@/utils/constants"
import { ProfileItem } from './ProfileItem'
import { ProfileItem } from "./ProfileItem"
export interface ProfileListProps {
cookiesStore: CookiesStore
@ -22,25 +22,25 @@ export const ProfileList = (props: ProfileListProps): JSX.Element => {
}, [])
return (
<ul className='m-0 list-none p-0'>
<ul className="m-0 list-none p-0">
<ProfileItem
title={i18n.translate('home.about.pronouns')}
value={i18n.translate('home.about.pronouns-value')}
title={i18n.translate("home.about.pronouns")}
value={i18n.translate("home.about.pronouns-value")}
/>
<ProfileItem
title={i18n.translate('home.about.birth-date')}
title={i18n.translate("home.about.birth-date")}
value={`${BIRTH_DATE_STRING} (${age} ${i18n.translate(
'home.about.years-old'
"home.about.years-old",
)})`}
/>
<ProfileItem
title={i18n.translate('home.about.nationality')}
value='Alsace, France'
title={i18n.translate("home.about.nationality")}
value="Alsace, France"
/>
<ProfileItem
title='Email'
value='contact@theoludwig.fr'
link='mailto:contact@theoludwig.fr'
title="Email"
value="contact@theoludwig.fr"
link="mailto:contact@theoludwig.fr"
/>
</ul>
)

View File

@ -1,11 +1,11 @@
import Image from 'next/image'
import Image from "next/image"
import Logo from 'public/images/logo.png'
import Logo from "public/images/logo.png"
export const ProfileLogo = (): JSX.Element => {
return (
<div className='max-h-[370px] max-w-[370px] px-2 py-6'>
<Image quality={100} src={Logo} alt='Théo LUDWIG' priority />
<div className="max-h-[370px] max-w-[370px] px-2 py-6">
<Image quality={100} src={Logo} alt="Théo LUDWIG" priority />
</div>
)
}

View File

@ -1,12 +1,12 @@
import { Icon } from './Icon'
import { Icon } from "./Icon"
export const EmailIcon = (
props: React.SVGProps<SVGSVGElement>
props: React.SVGProps<SVGSVGElement>,
): JSX.Element => {
return (
<Icon {...props}>
<title>Email</title>
<path d='M15.61 12c0 1.99-1.62 3.61-3.61 3.61-1.99 0-3.61-1.62-3.61-3.61 0-1.99 1.62-3.61 3.61-3.61 1.99 0 3.61 1.62 3.61 3.61M12 0C5.383 0 0 5.383 0 12s5.383 12 12 12c2.424 0 4.761-.722 6.76-2.087l.034-.024-1.617-1.879-.027.017A9.494 9.494 0 0112 21.54c-5.26 0-9.54-4.28-9.54-9.54 0-5.26 4.28-9.54 9.54-9.54 5.26 0 9.54 4.28 9.54 9.54a9.63 9.63 0 01-.225 2.05c-.301 1.239-1.169 1.618-1.82 1.568-.654-.053-1.42-.52-1.426-1.661V12A6.076 6.076 0 0012 5.93 6.076 6.076 0 005.93 12 6.076 6.076 0 0012 18.07a6.02 6.02 0 004.3-1.792 3.9 3.9 0 003.32 1.805c.874 0 1.74-.292 2.437-.821.719-.547 1.256-1.336 1.553-2.285.047-.154.135-.504.135-.507l.002-.013c.175-.76.253-1.52.253-2.457 0-6.617-5.383-12-12-12' />
<path d="M15.61 12c0 1.99-1.62 3.61-3.61 3.61-1.99 0-3.61-1.62-3.61-3.61 0-1.99 1.62-3.61 3.61-3.61 1.99 0 3.61 1.62 3.61 3.61M12 0C5.383 0 0 5.383 0 12s5.383 12 12 12c2.424 0 4.761-.722 6.76-2.087l.034-.024-1.617-1.879-.027.017A9.494 9.494 0 0112 21.54c-5.26 0-9.54-4.28-9.54-9.54 0-5.26 4.28-9.54 9.54-9.54 5.26 0 9.54 4.28 9.54 9.54a9.63 9.63 0 01-.225 2.05c-.301 1.239-1.169 1.618-1.82 1.568-.654-.053-1.42-.52-1.426-1.661V12A6.076 6.076 0 0012 5.93 6.076 6.076 0 005.93 12 6.076 6.076 0 0012 18.07a6.02 6.02 0 004.3-1.792 3.9 3.9 0 003.32 1.805c.874 0 1.74-.292 2.437-.821.719-.547 1.256-1.336 1.553-2.285.047-.154.135-.504.135-.507l.002-.013c.175-.76.253-1.52.253-2.457 0-6.617-5.383-12-12-12" />
</Icon>
)
}

View File

@ -1,12 +1,12 @@
import { Icon } from './Icon'
import { Icon } from "./Icon"
export const GitHubIcon = (
props: React.SVGProps<SVGSVGElement>
props: React.SVGProps<SVGSVGElement>,
): JSX.Element => {
return (
<Icon {...props}>
<title>GitHub</title>
<path d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12' />
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
</Icon>
)
}

View File

@ -1,12 +1,12 @@
import { Icon } from './Icon'
import { Icon } from "./Icon"
export const GitLabIcon = (
props: React.SVGProps<SVGSVGElement>
props: React.SVGProps<SVGSVGElement>,
): JSX.Element => {
return (
<Icon {...props}>
<title>GitLab</title>
<path d='M4.845.904c-.435 0-.82.28-.955.692C2.639 5.449 1.246 9.728.07 13.335a1.437 1.437 0 00.522 1.607l11.071 8.045c.2.145.472.144.67-.004l11.073-8.04a1.436 1.436 0 00.522-1.61c-1.285-3.942-2.683-8.256-3.817-11.746a1.004 1.004 0 00-.957-.684.987.987 0 00-.949.69l-2.405 7.408H8.203l-2.41-7.408a.987.987 0 00-.942-.69h-.006zm-.006 1.42l2.173 6.678H2.675zm14.326 0l2.168 6.678h-4.341zm-10.593 7.81h6.862c-1.142 3.52-2.288 7.04-3.434 10.559L8.572 10.135zm-5.514.005h4.321l3.086 9.5zm13.567 0h4.325c-2.467 3.17-4.95 6.328-7.411 9.502 1.028-3.167 2.059-6.334 3.086-9.502zM2.1 10.762l6.977 8.947-7.817-5.682a.305.305 0 01-.112-.341zm19.798 0l.952 2.922a.305.305 0 01-.11.341v.002l-7.82 5.68.026-.035z' />
<path d="M4.845.904c-.435 0-.82.28-.955.692C2.639 5.449 1.246 9.728.07 13.335a1.437 1.437 0 00.522 1.607l11.071 8.045c.2.145.472.144.67-.004l11.073-8.04a1.436 1.436 0 00.522-1.61c-1.285-3.942-2.683-8.256-3.817-11.746a1.004 1.004 0 00-.957-.684.987.987 0 00-.949.69l-2.405 7.408H8.203l-2.41-7.408a.987.987 0 00-.942-.69h-.006zm-.006 1.42l2.173 6.678H2.675zm14.326 0l2.168 6.678h-4.341zm-10.593 7.81h6.862c-1.142 3.52-2.288 7.04-3.434 10.559L8.572 10.135zm-5.514.005h4.321l3.086 9.5zm13.567 0h4.325c-2.467 3.17-4.95 6.328-7.411 9.502 1.028-3.167 2.059-6.334 3.086-9.502zM2.1 10.762l6.977 8.947-7.817-5.682a.305.305 0 01-.112-.341zm19.798 0l.952 2.922a.305.305 0 01-.11.341v.002l-7.82 5.68.026-.035z" />
</Icon>
)
}

View File

@ -1,15 +1,15 @@
import classNames from 'clsx'
import classNames from "clsx"
export const Icon = (props: React.SVGProps<SVGSVGElement>): JSX.Element => {
const { children, className, ...rest } = props
return (
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 24 24'
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
className={classNames(
'h-8 w-8 fill-current text-black dark:text-white',
className
"h-8 w-8 fill-current text-black dark:text-white",
className,
)}
{...rest}
>

View File

@ -1,10 +1,10 @@
import { Icon } from './Icon'
import { Icon } from "./Icon"
export const NPMIcon = (props: React.SVGProps<SVGSVGElement>): JSX.Element => {
return (
<Icon {...props}>
<title>npm</title>
<path d='M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z' />
<path d="M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z" />
</Icon>
)
}

View File

@ -1,12 +1,12 @@
import { Icon } from './Icon'
import { Icon } from "./Icon"
export const TwitchIcon = (
props: React.SVGProps<SVGSVGElement>
props: React.SVGProps<SVGSVGElement>,
): JSX.Element => {
return (
<Icon {...props}>
<title>Twitch</title>
<path d='M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714z' />
<path d="M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714z" />
</Icon>
)
}

View File

@ -1,12 +1,12 @@
import { Icon } from './Icon'
import { Icon } from "./Icon"
export const TwitterIcon = (
props: React.SVGProps<SVGSVGElement>
props: React.SVGProps<SVGSVGElement>,
): JSX.Element => {
return (
<Icon {...props}>
<title>Twitter</title>
<path d='M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z' />
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z" />
</Icon>
)
}

View File

@ -1,12 +1,12 @@
import { Icon } from './Icon'
import { Icon } from "./Icon"
export const YouTubeIcon = (
props: React.SVGProps<SVGSVGElement>
props: React.SVGProps<SVGSVGElement>,
): JSX.Element => {
return (
<Icon {...props}>
<title>YouTube</title>
<path d='M23.498 6.186a3.016 3.016 0 00-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 00.502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 002.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 002.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z' />
<path d="M23.498 6.186a3.016 3.016 0 00-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 00.502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 002.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 002.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
</Icon>
)
}

View File

@ -7,13 +7,13 @@ export const SocialMediaItem = (props: SocialMediaItemProps): JSX.Element => {
const { link, ariaLabel, children } = props
return (
<li className='mx-4 my-1 inline-block'>
<li className="mx-4 my-1 inline-block">
<a
href={link}
aria-label={ariaLabel}
target='_blank'
rel='noopener noreferrer'
className='relative inline-block bg-transparent'
target="_blank"
rel="noopener noreferrer"
className="relative inline-block bg-transparent"
>
{children}
</a>

View File

@ -1,43 +1,43 @@
import { SocialMediaItem } from './SocialMediaItem'
import { TwitterIcon } from './SocialMediaIcons/TwitterIcon'
import { GitHubIcon } from './SocialMediaIcons/GitHubIcon'
import { GitLabIcon } from './SocialMediaIcons/GitLabIcon'
import { YouTubeIcon } from './SocialMediaIcons/YouTubeIcon'
import { TwitchIcon } from './SocialMediaIcons/TwitchIcon'
import { EmailIcon } from './SocialMediaIcons/EmailIcon'
import { NPMIcon } from './SocialMediaIcons/NPMIcon'
import { SocialMediaItem } from "./SocialMediaItem"
import { TwitterIcon } from "./SocialMediaIcons/TwitterIcon"
import { GitHubIcon } from "./SocialMediaIcons/GitHubIcon"
import { GitLabIcon } from "./SocialMediaIcons/GitLabIcon"
import { YouTubeIcon } from "./SocialMediaIcons/YouTubeIcon"
import { TwitchIcon } from "./SocialMediaIcons/TwitchIcon"
import { EmailIcon } from "./SocialMediaIcons/EmailIcon"
import { NPMIcon } from "./SocialMediaIcons/NPMIcon"
export const SocialMediaList = (): JSX.Element => {
return (
<ul className='social-media-list m-0 mt-2 list-none py-4 text-center'>
<SocialMediaItem link='https://github.com/theoludwig' ariaLabel='GitHub'>
<ul className="social-media-list m-0 mt-2 list-none py-4 text-center">
<SocialMediaItem link="https://github.com/theoludwig" ariaLabel="GitHub">
<GitHubIcon />
</SocialMediaItem>
<SocialMediaItem link='https://gitlab.com/theoludwig' ariaLabel='GitLab'>
<SocialMediaItem link="https://gitlab.com/theoludwig" ariaLabel="GitLab">
<GitLabIcon />
</SocialMediaItem>
<SocialMediaItem link='https://www.npmjs.com/~theoludwig' ariaLabel='npm'>
<SocialMediaItem link="https://www.npmjs.com/~theoludwig" ariaLabel="npm">
<NPMIcon />
</SocialMediaItem>
<SocialMediaItem
link='https://twitter.com/theoludwig_'
ariaLabel='Twitter'
link="https://twitter.com/theoludwig_"
ariaLabel="Twitter"
>
<TwitterIcon />
</SocialMediaItem>
<SocialMediaItem
link='https://www.youtube.com/@theo_ludwig'
ariaLabel='YouTube'
link="https://www.youtube.com/@theo_ludwig"
ariaLabel="YouTube"
>
<YouTubeIcon />
</SocialMediaItem>
<SocialMediaItem
link='https://www.twitch.tv/theoludwig'
ariaLabel='Twitch'
link="https://www.twitch.tv/theoludwig"
ariaLabel="Twitch"
>
<TwitchIcon />
</SocialMediaItem>
<SocialMediaItem link='mailto:contact@theoludwig.fr' ariaLabel='Email'>
<SocialMediaItem link="mailto:contact@theoludwig.fr" ariaLabel="Email">
<EmailIcon />
</SocialMediaItem>
</ul>

View File

@ -1,15 +1,15 @@
import { cookies } from 'next/headers'
import { cookies } from "next/headers"
import { ProfileDescriptionBottom } from './ProfileDescriptionBottom'
import { ProfileInformation } from './ProfileInfo'
import { ProfileList } from './ProfileList'
import { ProfileLogo } from './ProfileLogo'
import { ProfileDescriptionBottom } from "./ProfileDescriptionBottom"
import { ProfileInformation } from "./ProfileInfo"
import { ProfileList } from "./ProfileList"
import { ProfileLogo } from "./ProfileLogo"
export const Profile = (): JSX.Element => {
const cookiesStore = cookies()
return (
<div className='flex flex-col items-center justify-center px-10 pt-2 md:flex-row md:pt-10'>
<div className="flex flex-col items-center justify-center px-10 pt-2 md:flex-row md:pt-10">
<ProfileLogo />
<div>
<ProfileInformation />

View File

@ -1,9 +1,9 @@
import Image from 'next/image'
import Image from "next/image"
import { getTheme } from '@/theme/theme.server'
import { getTheme } from "@/theme/theme.server"
import type { SkillName } from './skills'
import { skills } from './skills'
import type { SkillName } from "./skills"
import { skills } from "./skills"
export interface SkillComponentProps {
skill: SkillName
@ -17,10 +17,10 @@ export const SkillComponent = (props: SkillComponentProps): JSX.Element => {
const theme = getTheme()
const getImage = (): string => {
if (typeof skillProperties.image === 'string') {
if (typeof skillProperties.image === "string") {
return skillProperties.image
}
if (theme === 'light') {
if (theme === "light") {
return skillProperties.image.light
}
return skillProperties.image.dark
@ -29,20 +29,20 @@ export const SkillComponent = (props: SkillComponentProps): JSX.Element => {
return (
<a
href={skillProperties.link}
className='mx-2 max-w-xl text-yellow hover:underline dark:text-yellow-dark'
target='_blank'
rel='noopener noreferrer'
className="mx-2 max-w-xl text-yellow hover:underline dark:text-yellow-dark"
target="_blank"
rel="noopener noreferrer"
>
<div className='text-center'>
<div className="text-center">
<Image
className='inline h-16 w-16'
className="inline h-16 w-16"
quality={100}
width={64}
height={64}
alt={skill}
src={getImage()}
/>
<p className='mt-1'>{skill}</p>
<p className="mt-1">{skill}</p>
</div>
</a>
)

View File

@ -1,4 +1,4 @@
import { ShadowContainer } from '@/components/design/ShadowContainer'
import { ShadowContainer } from "@/components/design/ShadowContainer"
export interface SkillsSectionProps {
title: string
@ -10,15 +10,15 @@ export const SkillsSection = (props: SkillsSectionProps): JSX.Element => {
return (
<ShadowContainer>
<div className='mx-auto w-full px-4'>
<div className='flex flex-wrap px-4 py-6'>
<div className='flex-1'>
<div className='mb-8 border-b border-gray-600 dark:border-white dark:border-opacity-10'>
<h3 className='my-3 text-xl font-semibold text-yellow dark:text-yellow-dark'>
<div className="mx-auto w-full px-4">
<div className="flex flex-wrap px-4 py-6">
<div className="flex-1">
<div className="mb-8 border-b border-gray-600 dark:border-white dark:border-opacity-10">
<h3 className="my-3 text-xl font-semibold text-yellow dark:text-yellow-dark">
{title}
</h3>
</div>
<div className='flex flex-wrap justify-around'>{children}</div>
<div className="flex flex-wrap justify-around">{children}</div>
</div>
</div>
</div>

View File

@ -1,40 +1,40 @@
import { getI18n } from '@/i18n/i18n.server'
import { getI18n } from "@/i18n/i18n.server"
import { SkillComponent } from './Skill'
import { SkillsSection } from './SkillsSection'
import { SkillComponent } from "./Skill"
import { SkillsSection } from "./SkillsSection"
export const Skills = (): JSX.Element => {
const i18n = getI18n()
return (
<>
<SkillsSection title={i18n.translate('home.skills.languages')}>
<SkillComponent skill='TypeScript' />
<SkillComponent skill='Python' />
<SkillComponent skill='C/C++' />
<SkillComponent skill='PHP' />
<SkillsSection title={i18n.translate("home.skills.languages")}>
<SkillComponent skill="TypeScript" />
<SkillComponent skill="Python" />
<SkillComponent skill="C/C++" />
<SkillComponent skill="PHP" />
</SkillsSection>
<SkillsSection title='Frontend'>
<SkillComponent skill='HTML' />
<SkillComponent skill='CSS' />
<SkillComponent skill='Tailwind CSS' />
<SkillComponent skill='React.js (+ Next.js)' />
<SkillsSection title="Frontend">
<SkillComponent skill="HTML" />
<SkillComponent skill="CSS" />
<SkillComponent skill="Tailwind CSS" />
<SkillComponent skill="React.js (+ Next.js)" />
</SkillsSection>
<SkillsSection title='Backend'>
<SkillComponent skill='Laravel' />
<SkillComponent skill='Node.js' />
<SkillComponent skill='Fastify' />
<SkillComponent skill='PostgreSQL' />
<SkillsSection title="Backend">
<SkillComponent skill="Laravel" />
<SkillComponent skill="Node.js" />
<SkillComponent skill="Fastify" />
<SkillComponent skill="PostgreSQL" />
</SkillsSection>
<SkillsSection title={i18n.translate('home.skills.software-tools')}>
<SkillComponent skill='GNU/Linux' />
<SkillComponent skill='Arch Linux' />
<SkillComponent skill='Visual Studio Code' />
<SkillComponent skill='Git' />
<SkillComponent skill='Docker' />
<SkillsSection title={i18n.translate("home.skills.software-tools")}>
<SkillComponent skill="GNU/Linux" />
<SkillComponent skill="Arch Linux" />
<SkillComponent skill="Visual Studio Code" />
<SkillComponent skill="Git" />
<SkillComponent skill="Docker" />
</SkillsSection>
</>
)

View File

@ -5,111 +5,111 @@ export interface Skill {
export const skills = {
JavaScript: {
link: 'https://developer.mozilla.org/docs/Web/JavaScript',
image: '/images/skills/JavaScript.png'
link: "https://developer.mozilla.org/docs/Web/JavaScript",
image: "/images/skills/JavaScript.png",
},
TypeScript: {
link: 'https://www.typescriptlang.org/',
image: '/images/skills/TypeScript.png'
link: "https://www.typescriptlang.org/",
image: "/images/skills/TypeScript.png",
},
Python: {
link: 'https://www.python.org/',
image: '/images/skills/Python.png'
link: "https://www.python.org/",
image: "/images/skills/Python.png",
},
'C/C++': {
link: 'https://isocpp.org/',
image: '/images/skills/C-Cpp.png'
"C/C++": {
link: "https://isocpp.org/",
image: "/images/skills/C-Cpp.png",
},
PHP: {
link: 'https://www.php.net/',
image: '/images/skills/PHP.png'
link: "https://www.php.net/",
image: "/images/skills/PHP.png",
},
Laravel: {
link: 'https://laravel.com/',
image: '/images/skills/Laravel.png'
link: "https://laravel.com/",
image: "/images/skills/Laravel.png",
},
Dart: {
link: 'https://dart.dev/',
image: '/images/skills/Dart.png'
link: "https://dart.dev/",
image: "/images/skills/Dart.png",
},
Flutter: {
link: 'https://flutter.dev/',
image: '/images/skills/Flutter.webp'
link: "https://flutter.dev/",
image: "/images/skills/Flutter.webp",
},
HTML: {
link: 'https://developer.mozilla.org/docs/Web/HTML',
image: '/images/skills/HTML.png'
link: "https://developer.mozilla.org/docs/Web/HTML",
image: "/images/skills/HTML.png",
},
CSS: {
link: 'https://developer.mozilla.org/docs/Web/CSS',
image: '/images/skills/CSS.png'
link: "https://developer.mozilla.org/docs/Web/CSS",
image: "/images/skills/CSS.png",
},
'Tailwind CSS': {
link: 'https://tailwindcss.com/',
image: '/images/skills/TailwindCSS.png'
"Tailwind CSS": {
link: "https://tailwindcss.com/",
image: "/images/skills/TailwindCSS.png",
},
SASS: {
link: 'https://sass-lang.com/',
image: '/images/skills/SASS.svg'
link: "https://sass-lang.com/",
image: "/images/skills/SASS.svg",
},
'React.js (+ Next.js)': {
link: 'https://reactjs.org/',
image: '/images/skills/ReactJS.png'
"React.js (+ Next.js)": {
link: "https://reactjs.org/",
image: "/images/skills/ReactJS.png",
},
'Node.js': {
link: 'https://nodejs.org/',
image: '/images/skills/NodeJS.png'
"Node.js": {
link: "https://nodejs.org/",
image: "/images/skills/NodeJS.png",
},
Fastify: {
link: 'https://www.fastify.io/',
link: "https://www.fastify.io/",
image: {
light: '/images/skills/Fastify-light.png',
dark: '/images/skills/Fastify-dark.png'
}
light: "/images/skills/Fastify-light.png",
dark: "/images/skills/Fastify-dark.png",
},
},
Prisma: {
link: 'https://www.prisma.io/',
link: "https://www.prisma.io/",
image: {
light: '/images/skills/Prisma-light.png',
dark: '/images/skills/Prisma-dark.png'
}
light: "/images/skills/Prisma-light.png",
dark: "/images/skills/Prisma-dark.png",
},
},
PostgreSQL: {
link: 'https://www.postgresql.org/',
image: '/images/skills/PostgreSQL.png'
link: "https://www.postgresql.org/",
image: "/images/skills/PostgreSQL.png",
},
MySQL: {
link: 'https://www.mysql.com/',
image: '/images/skills/MySQL.png'
link: "https://www.mysql.com/",
image: "/images/skills/MySQL.png",
},
Strapi: {
link: 'https://strapi.io/',
image: '/images/skills/Strapi.png'
link: "https://strapi.io/",
image: "/images/skills/Strapi.png",
},
'Visual Studio Code': {
link: 'https://code.visualstudio.com/',
image: '/images/skills/VisualStudioCode.png'
"Visual Studio Code": {
link: "https://code.visualstudio.com/",
image: "/images/skills/VisualStudioCode.png",
},
Git: {
link: 'https://git-scm.com/',
image: '/images/skills/Git.png'
link: "https://git-scm.com/",
image: "/images/skills/Git.png",
},
Ubuntu: {
link: 'https://ubuntu.com/',
image: '/images/skills/Ubuntu.png'
link: "https://ubuntu.com/",
image: "/images/skills/Ubuntu.png",
},
'Arch Linux': {
link: 'https://archlinux.org/',
image: '/images/skills/ArchLinux.png'
"Arch Linux": {
link: "https://archlinux.org/",
image: "/images/skills/ArchLinux.png",
},
'GNU/Linux': {
link: 'https://www.gnu.org/',
image: '/images/skills/GNU-Linux.png'
"GNU/Linux": {
link: "https://www.gnu.org/",
image: "/images/skills/GNU-Linux.png",
},
Docker: {
link: 'https://www.docker.com/',
image: '/images/skills/Docker.png'
}
link: "https://www.docker.com/",
image: "/images/skills/Docker.png",
},
} as const
export type SkillName = keyof typeof skills

View File

@ -1,4 +1,4 @@
import classNames from 'clsx'
import classNames from "clsx"
export interface LoaderProps {
width?: number
@ -13,16 +13,16 @@ export const Loader = (props: LoaderProps): JSX.Element => {
<div
style={{
width,
height
height,
}}
className={classNames(
'animate-spin inline-block border-[3px] border-current border-t-transparent text-yellow dark:text-yellow-dark rounded-full',
className
"animate-spin inline-block border-[3px] border-current border-t-transparent text-yellow dark:text-yellow-dark rounded-full",
className,
)}
role='status'
aria-label='loading'
role="status"
aria-label="loading"
>
<span className='sr-only'>Loading...</span>
<span className="sr-only">Loading...</span>
</div>
)
}

View File

@ -1,6 +1,6 @@
'use client'
"use client"
import { useEffect, useRef } from 'react'
import { useEffect, useRef } from "react"
export type RevealFadeProps = React.PropsWithChildren
@ -15,22 +15,22 @@ export const RevealFade = (props: RevealFadeProps): JSX.Element => {
for (const entry of entries) {
if (entry.isIntersecting) {
entry.target.className =
'opacity-100 visible translate-y-0 transition-all duration-700 ease-in-out'
"opacity-100 visible translate-y-0 transition-all duration-700 ease-in-out"
observer.unobserve(entry.target)
}
}
},
{
root: null,
rootMargin: '0px',
threshold: 0.28
}
rootMargin: "0px",
threshold: 0.28,
},
)
observer.observe(htmlElement.current as HTMLDivElement)
}, [])
return (
<div ref={htmlElement} className='invisible -translate-y-7 opacity-0'>
<div ref={htmlElement} className="invisible -translate-y-7 opacity-0">
{children}
</div>
)

View File

@ -1,10 +1,10 @@
type SectionHeadingProps = React.ComponentPropsWithRef<'h2'>
type SectionHeadingProps = React.ComponentPropsWithRef<"h2">
export const SectionHeading = (props: SectionHeadingProps): JSX.Element => {
const { children, ...rest } = props
return (
<h2 {...rest} className='mb-3 mt-1 text-center text-4xl font-semibold'>
<h2 {...rest} className="mb-3 mt-1 text-center text-4xl font-semibold">
{children}
</h2>
)

View File

@ -1,7 +1,7 @@
import { ShadowContainer } from '@/components/design/ShadowContainer'
import { SectionHeading } from '@/components/design/Section/SectionHeading'
import { ShadowContainer } from "@/components/design/ShadowContainer"
import { SectionHeading } from "@/components/design/Section/SectionHeading"
type SectionProps = React.ComponentPropsWithRef<'section'> & {
type SectionProps = React.ComponentPropsWithRef<"section"> & {
heading?: string
description?: string
isMain?: boolean
@ -20,13 +20,13 @@ export const Section = (props: SectionProps): JSX.Element => {
if (isMain) {
return (
<div className='w-full px-3'>
<div className="w-full px-3">
<ShadowContainer style={{ marginTop: 50 }}>
<section {...rest}>
{heading != null ? (
<SectionHeading>{heading}</SectionHeading>
) : null}
<div className='w-full px-3'>{children}</div>
<div className="w-full px-3">{children}</div>
</section>
</ShadowContainer>
</div>
@ -37,7 +37,7 @@ export const Section = (props: SectionProps): JSX.Element => {
return (
<section {...rest}>
{heading != null ? <SectionHeading>{heading}</SectionHeading> : null}
<div className='w-full px-3'>{children}</div>
<div className="w-full px-3">{children}</div>
</section>
)
}
@ -52,13 +52,13 @@ export const Section = (props: SectionProps): JSX.Element => {
</SectionHeading>
) : null}
{description != null ? (
<p style={{ marginTop: 7 }} className='text-center'>
<p style={{ marginTop: 7 }} className="text-center">
{description}
</p>
) : null}
<div className='w-full px-3'>
<div className="w-full px-3">
<ShadowContainer>
<div className='w-full px-16 py-4 leading-8'>{children}</div>
<div className="w-full px-16 py-4 leading-8">{children}</div>
</ShadowContainer>
</div>
</section>

View File

@ -1,6 +1,6 @@
import classNames from 'clsx'
import classNames from "clsx"
type ShadowContainerProps = React.ComponentPropsWithRef<'div'>
type ShadowContainerProps = React.ComponentPropsWithRef<"div">
export const ShadowContainer = (props: ShadowContainerProps): JSX.Element => {
const { children, className, ...rest } = props
@ -8,8 +8,8 @@ export const ShadowContainer = (props: ShadowContainerProps): JSX.Element => {
return (
<div
className={classNames(
'mb-12 h-full max-w-full break-words rounded-2xl border border-solid border-[#000] shadow-light dark:shadow-dark ',
className
"mb-12 h-full max-w-full break-words rounded-2xl border border-solid border-[#000] shadow-light dark:shadow-dark ",
className,
)}
{...rest}
>