1
1
mirror of https://github.com/theoludwig/theoludwig.git synced 2025-05-21 23:48:36 +02:00

Compare commits

...

3 Commits

52 changed files with 3983 additions and 3695 deletions

View File

@ -9,3 +9,6 @@ end_of_line = lf
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.md]
indent_size = 4

View File

@ -6,8 +6,8 @@ labels: "bug"
--- ---
<!-- <!--
Please provide a clear and concise description of what the bug is. Include Please provide a clear and concise description of what the bug is. Include
screenshots if needed. Please make sure your issue has not already been fixed. screenshots if needed. Please make sure your issue has not already been fixed.
--> -->
## Steps To Reproduce ## Steps To Reproduce

View File

@ -24,7 +24,7 @@ jobs:
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.2.0" uses: "actions/setup-node@v4.2.0"
with: with:
node-version: "22.x" node-version: "24.x"
cache: "pnpm" cache: "pnpm"
- name: "Install dependencies" - name: "Install dependencies"

View File

@ -22,7 +22,7 @@ jobs:
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.2.0" uses: "actions/setup-node@v4.2.0"
with: with:
node-version: "22.x" node-version: "24.x"
cache: "pnpm" cache: "pnpm"
- name: "Install dependencies" - name: "Install dependencies"

View File

@ -1,4 +1,4 @@
import relativeLinksRule from "markdownlint-rule-relative-links" import relativeLinksRule, { markdownIt } from "markdownlint-rule-relative-links"
const config = { const config = {
config: { config: {
@ -11,6 +11,9 @@ const config = {
globs: ["**/*.md"], globs: ["**/*.md"],
ignores: ["**/node_modules"], ignores: ["**/node_modules"],
customRules: [relativeLinksRule], customRules: [relativeLinksRule],
markdownItFactory: () => {
return markdownIt
},
} }
export default config export default config

View File

@ -6,7 +6,6 @@
"davidanson.vscode-markdownlint", "davidanson.vscode-markdownlint",
"bradlc.vscode-tailwindcss", "bradlc.vscode-tailwindcss",
"mikestead.dotenv", "mikestead.dotenv",
"ms-azuretools.vscode-docker",
"antfu.pnpm-catalog-lens", "antfu.pnpm-catalog-lens",
"Lokalise.i18n-ally" "Lokalise.i18n-ally"
] ]

View File

@ -20,21 +20,16 @@ community include:
- Demonstrating empathy and kindness toward other people - Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences - Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback - Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes, - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall community
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include: Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or - The use of sexualized language or imagery, and sexual attention or advances of any kind
advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks - Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment - Public or private harassment
- Publishing others' private information, such as a physical or email - Publishing others' private information, such as a physical or email address, without their explicit permission
address, without their explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities ## Enforcement Responsibilities

View File

@ -31,8 +31,8 @@ The commit message guidelines adheres to [Conventional Commits](https://www.conv
### Prerequisites ### Prerequisites
- [Node.js](https://nodejs.org/) >= 22.12.0 [(`nvm install 22`)](https://nvm.sh) - [Node.js](https://nodejs.org/) >= v24.0.0 [(`nvm install 24`)](https://nvm.sh)
- [pnpm](https://pnpm.io/) >= 10.2.1 [(`corepack enable`)](https://nodejs.org/docs/latest-v22.x/api/corepack.html) - [pnpm](https://pnpm.io/) v10.10.0 [(`npm install --global corepack@0.32.0 && corepack enable`)](https://github.com/nodejs/corepack)
- [Docker](https://www.docker.com/) - [Docker](https://www.docker.com/)
### Installation ### Installation

View File

@ -1,18 +1,18 @@
<h1 align="center"><a href="https://theoludwig.fr/">Théo LUDWIG</a></h1> <h1 align="center"><a href="https://theoludwig.fr/">Théo LUDWIG</a></h1>
<p align="center"> <p align="center">
<strong>Developer Full Stack • Open-Source Enthusiast</strong> <strong>Developer Full Stack • Open-Source Enthusiast</strong>
</p> </p>
<p align="center"> <p align="center">
<a href="https://github.com/theoludwig"><img alt="GitHub" src="https://img.shields.io/badge/-GitHub-5A5A5A?style=flat&labelColor=5A5A5A&logo=github&logoColor=white"/></a> <a href="https://github.com/theoludwig"><img alt="GitHub" src="https://img.shields.io/badge/-GitHub-5A5A5A?style=flat&labelColor=5A5A5A&logo=github&logoColor=white"/></a>
<a href="https://gitlab.com/theoludwig"><img alt="GitLab" src="https://img.shields.io/badge/-GitLab-303030?style=flat&labelColor=303030&logo=gitlab&logoColor=white"/></a> <a href="https://gitlab.com/theoludwig"><img alt="GitLab" src="https://img.shields.io/badge/-GitLab-303030?style=flat&labelColor=303030&logo=gitlab&logoColor=white"/></a>
<a href="https://www.npmjs.com/~theoludwig"><img alt="npm" src="https://img.shields.io/badge/-npm-c4302b?style=flat&labelColor=c4302b&logo=npm&logoColor=white"/></a> <a href="https://www.npmjs.com/~theoludwig"><img alt="npm" src="https://img.shields.io/badge/-npm-c4302b?style=flat&labelColor=c4302b&logo=npm&logoColor=white"/></a>
<a href="https://twitter.com/theoludwig_"><img alt="Twitter" src="https://img.shields.io/badge/-Twitter-1ca0f1?style=flat&labelColor=1ca0f1&logo=x&logoColor=white"/></a> <a href="https://twitter.com/theoludwig_"><img alt="Twitter" src="https://img.shields.io/badge/-Twitter-1ca0f1?style=flat&labelColor=1ca0f1&logo=x&logoColor=white"/></a>
<a href="https://www.youtube.com/@theo_ludwig"><img alt="YouTube" src="https://img.shields.io/badge/-YouTube-c4302b?style=flat&labelColor=c4302b&logo=youtube&logoColor=white"/></a> <a href="https://www.youtube.com/@theo_ludwig"><img alt="YouTube" src="https://img.shields.io/badge/-YouTube-c4302b?style=flat&labelColor=c4302b&logo=youtube&logoColor=white"/></a>
<a href="https://www.twitch.tv/theoludwig"><img alt="Twitch" src="https://img.shields.io/badge/-Twitch-9147FF?style=flat&labelColor=9147FF&logo=twitch&logoColor=white"/></a> <a href="https://www.twitch.tv/theoludwig"><img alt="Twitch" src="https://img.shields.io/badge/-Twitch-9147FF?style=flat&labelColor=9147FF&logo=twitch&logoColor=white"/></a>
<a href="https://theoludwig.fr/"><img alt="Website" src="https://img.shields.io/badge/-Website-181818?style=flat&labelColor=181818&logo=Google-Chrome&logoColor=white"/></a> <a href="https://theoludwig.fr/"><img alt="Website" src="https://img.shields.io/badge/-Website-181818?style=flat&labelColor=181818&logo=Google-Chrome&logoColor=white"/></a>
<a href="mailto:contact@theoludwig.fr"><img alt="Email" src="https://img.shields.io/badge/-contact@theoludwig.fr-2F7EBE?style=flat&labelColor=2F7EBE&logo=minutemailer&logoColor=white"/></a> <a href="mailto:contact@theoludwig.fr"><img alt="Email" src="https://img.shields.io/badge/-contact@theoludwig.fr-2F7EBE?style=flat&labelColor=2F7EBE&logo=minutemailer&logoColor=white"/></a>
</p> </p>
<hr /> <hr />
@ -21,17 +21,28 @@
```json ```json
{ {
"name": "Théo LUDWIG", "name": "Théo LUDWIG",
"pronouns": "He/Him", "pronouns": "He/Him",
"birthDate": "2003-03-31", "birthDate": "2003-03-31",
"nationality": "Alsace, France", "nationality": "Alsace, France",
"interests": ["Developer Full Stack", "Open-Source Enthusiast"], "interests": ["Developer Full Stack", "Open-Source Enthusiast"],
"skills": { "skills": {
"programmingLanguages": ["JavaScript/TypeScript", "Python", "C/C++", "PHP"], "programmingLanguages": [
"frontend": ["HTML/CSS", "Tailwind CSS", "React.js/Next.js"], "JavaScript/TypeScript",
"backend": ["Laravel", "Node.js", "Fastify", "PostgreSQL"], "Python",
"tools": ["GNU/Linux", "Arch Linux", "Visual Studio Code", "Git", "Docker"] "C/C++",
} "PHP"
],
"frontend": ["HTML/CSS", "Tailwind CSS", "React.js/Next.js"],
"backend": ["Laravel", "Node.js", "Fastify", "PostgreSQL"],
"tools": [
"GNU/Linux",
"Arch Linux",
"Visual Studio Code",
"Git",
"Docker"
]
}
} }
``` ```
@ -40,6 +51,6 @@
## 📈 Statistics ## 📈 Statistics
<p align=center> <p align=center>
<img height=175 align="center" src="https://github-readme-stats.vercel.app/api?username=theoludwig&show_icons=true&theme=dark" alt="Théo LUDWIG's GitHub Stats" /> <img height=175 align="center" src="https://github-readme-stats.vercel.app/api?username=theoludwig&show_icons=true&theme=dark" alt="Théo LUDWIG's GitHub Stats" />
<img height=175 align="center" src="https://github-readme-stats.vercel.app/api/top-langs/?username=theoludwig&hide=html,css,javascript&langs_count=8&layout=compact&theme=dark" alt="Théo LUDWIG's Programming Languages" /> <img height=175 align="center" src="https://github-readme-stats.vercel.app/api/top-langs/?username=theoludwig&hide=html,css,javascript&langs_count=8&layout=compact&theme=dark" alt="Théo LUDWIG's Programming Languages" />
</p> </p>

View File

@ -0,0 +1,5 @@
{
"plugins": {
"@tailwindcss/postcss": {}
}
}

View File

@ -51,6 +51,7 @@
"storybook-dark-mode": "catalog:", "storybook-dark-mode": "catalog:",
"postcss": "catalog:", "postcss": "catalog:",
"tailwindcss": "catalog:", "tailwindcss": "catalog:",
"@tailwindcss/postcss": "catalog:",
"typescript-eslint": "catalog:", "typescript-eslint": "catalog:",
"typescript": "catalog:" "typescript": "catalog:"
} }

View File

@ -1,7 +0,0 @@
const config = {
plugins: {
tailwindcss: {},
},
}
export default config

View File

@ -2,11 +2,6 @@ import sharedConfig from "@repo/config-tailwind"
/** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */ /** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */
const config = { const config = {
content: [
".storybook/preview.tsx",
"../../packages/ui/src/**/*.tsx",
"../../packages/blog/src/**/*.tsx",
],
presets: [sharedConfig], presets: [sharedConfig],
} }

View File

@ -0,0 +1,5 @@
{
"plugins": {
"@tailwindcss/postcss": {}
}
}

View File

@ -1,7 +1,7 @@
FROM node:22.13.1-slim AS node-pnpm FROM node:24.0.0-slim AS node-pnpm
ENV PNPM_HOME="/pnpm" ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH" ENV PATH="$PNPM_HOME:$PATH"
RUN npm install --global corepack@0.31.0 && corepack enable RUN npm install --global corepack@0.32.0 && corepack enable
ENV TURBO_TELEMETRY_DISABLED=1 ENV TURBO_TELEMETRY_DISABLED=1
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
ENV DO_NOT_TRACK=1 ENV DO_NOT_TRACK=1
@ -9,7 +9,7 @@ WORKDIR /usr/src/app
FROM node-pnpm AS builder FROM node-pnpm AS builder
COPY ./ ./ COPY ./ ./
RUN pnpm install --global turbo@2.4.0 RUN pnpm install --global turbo@2.5.3
RUN turbo prune @repo/website --docker RUN turbo prune @repo/website --docker
FROM node-pnpm AS installer FROM node-pnpm AS installer

View File

@ -37,6 +37,7 @@
"eslint": "catalog:", "eslint": "catalog:",
"postcss": "catalog:", "postcss": "catalog:",
"tailwindcss": "catalog:", "tailwindcss": "catalog:",
"@tailwindcss/postcss": "catalog:",
"typescript-eslint": "catalog:", "typescript-eslint": "catalog:",
"typescript": "catalog:" "typescript": "catalog:"
} }

View File

@ -1,7 +0,0 @@
const config = {
plugins: {
tailwindcss: {},
},
}
export default config

Binary file not shown.

After

Width:  |  Height:  |  Size: 945 KiB

View File

@ -2,11 +2,6 @@ import sharedConfig from "@repo/config-tailwind"
/** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */ /** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */
const config = { const config = {
content: [
"./app/**/*.tsx",
"../../packages/ui/src/**/*.tsx",
"../../packages/blog/src/**/*.tsx",
],
presets: [sharedConfig], presets: [sharedConfig],
} }

View File

@ -1,16 +1,14 @@
import { FlatCompat } from "@eslint/eslintrc" import { FlatCompat } from "@eslint/eslintrc"
import storybook from "eslint-plugin-storybook"
import tailwind from "eslint-plugin-tailwindcss"
import typescriptESLint from "typescript-eslint" import typescriptESLint from "typescript-eslint"
import config from "../eslint.config.js" import config from "../eslint.config.js"
const flatCompat = new FlatCompat() const flatCompat = new FlatCompat({
baseDirectory: import.meta.dirname,
})
export default typescriptESLint.config( export default typescriptESLint.config(
...config, ...config,
...flatCompat.extends("next/core-web-vitals"), ...flatCompat.extends("next/core-web-vitals"),
...tailwind.configs["flat/recommended"],
...storybook.configs["flat/recommended"],
{ {
name: "config-eslint/nextjs", name: "config-eslint/nextjs",
settings: { settings: {
@ -22,8 +20,6 @@ export default typescriptESLint.config(
}, },
}, },
rules: { rules: {
"tailwindcss/classnames-order": "off",
"tailwindcss/no-custom-classname": "off",
"@next/next/no-html-link-for-pages": "off", "@next/next/no-html-link-for-pages": "off",
"@next/next/no-img-element": "off", "@next/next/no-img-element": "off",
"react/self-closing-comp": [ "react/self-closing-comp": [

View File

@ -25,8 +25,6 @@
"eslint-plugin-promise": "catalog:", "eslint-plugin-promise": "catalog:",
"eslint-plugin-unicorn": "catalog:", "eslint-plugin-unicorn": "catalog:",
"eslint-config-next": "catalog:", "eslint-config-next": "catalog:",
"eslint-plugin-storybook": "catalog:",
"eslint-plugin-tailwindcss": "catalog:",
"eslint-plugin-import-x": "catalog:", "eslint-plugin-import-x": "catalog:",
"typescript": "catalog:", "typescript": "catalog:",
"globals": "catalog:" "globals": "catalog:"

View File

@ -0,0 +1,5 @@
{
"plugins": {
"@tailwindcss/postcss": {}
}
}

View File

@ -1,4 +1,5 @@
import { type ClassValue, clsx } from "clsx" import type { ClassValue } from "clsx"
import { clsx } from "clsx"
import { twMerge } from "tailwind-merge" import { twMerge } from "tailwind-merge"
export const classNames = (...inputs: ClassValue[]): string => { export const classNames = (...inputs: ClassValue[]): string => {

View File

@ -27,6 +27,7 @@
"typescript-eslint": "catalog:", "typescript-eslint": "catalog:",
"eslint": "catalog:", "eslint": "catalog:",
"postcss": "catalog:", "postcss": "catalog:",
"tailwindcss": "catalog:" "tailwindcss": "catalog:",
"@tailwindcss/postcss": "catalog:"
} }
} }

View File

@ -1,7 +0,0 @@
const config = {
plugins: {
tailwindcss: {},
},
}
export default config

View File

@ -1,12 +1,16 @@
@import "@fontsource/montserrat/400.css"; @import "tailwindcss";
@import "@fontsource/montserrat/500.css"; @config "./tailwind.config.js";
@import "@fontsource/montserrat/600.css";
@import "@fontsource/montserrat/700.css";
@import "@fontsource/montserrat/800.css";
@tailwind base; @source "../../packages/ui";
@tailwind components; @source "../../packages/blog";
@tailwind utilities; @source "../../apps/website";
@source "../../apps/storybook/.storybook";
@import "@fontsource/montserrat/400.css" layer(base);
@import "@fontsource/montserrat/500.css" layer(base);
@import "@fontsource/montserrat/600.css" layer(base);
@import "@fontsource/montserrat/700.css" layer(base);
@import "@fontsource/montserrat/800.css" layer(base);
@layer base { @layer base {
[type="search"]::-webkit-search-decoration, [type="search"]::-webkit-search-decoration,
@ -14,6 +18,11 @@
appearance: none; appearance: none;
} }
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
b, b,
strong { strong {
@apply font-semibold; @apply font-semibold;

View File

@ -11,6 +11,7 @@
"noUncheckedIndexedAccess": true, "noUncheckedIndexedAccess": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"isolatedModules": true, "isolatedModules": true,

View File

@ -3,15 +3,14 @@
"version": "0.0.0-develop", "version": "0.0.0-develop",
"private": true, "private": true,
"type": "module", "type": "module",
"packageManager": "pnpm@10.2.1+sha512.398035c7bd696d0ba0b10a688ed558285329d27ea994804a52bad9167d8e3a72bcb993f9699585d3ca25779ac64949ef422757a6c31102c12ab932e5cbe5cc92", "packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
"engines": { "engines": {
"node": ">=22.12.0", "node": ">=24.0.0"
"pnpm": ">=10.2.1"
}, },
"scripts": { "scripts": {
"build": "turbo run build",
"dev": "turbo run dev --parallel", "dev": "turbo run dev --parallel",
"start": "turbo run start --parallel", "start": "turbo run start --parallel",
"build": "turbo run build",
"test": "turbo run test", "test": "turbo run test",
"lint:editorconfig": "editorconfig-checker", "lint:editorconfig": "editorconfig-checker",
"lint:markdown": "markdownlint-cli2", "lint:markdown": "markdownlint-cli2",

View File

@ -0,0 +1,5 @@
{
"plugins": {
"@tailwindcss/postcss": {}
}
}

View File

@ -49,6 +49,7 @@
"eslint": "catalog:", "eslint": "catalog:",
"postcss": "catalog:", "postcss": "catalog:",
"tailwindcss": "catalog:", "tailwindcss": "catalog:",
"@tailwindcss/postcss": "catalog:",
"typescript-eslint": "catalog:", "typescript-eslint": "catalog:",
"typescript": "catalog:" "typescript": "catalog:"
} }

View File

@ -1,7 +0,0 @@
const config = {
plugins: {
tailwindcss: {},
},
}
export default config

View File

@ -109,11 +109,11 @@ const transaction = charge(user, subscription)
```typescript ```typescript
interface Car { interface Car {
carModel: string carModel: string
carColor: "red" | "blue" | "yellow" carColor: "red" | "blue" | "yellow"
} }
const printCar = (car: Car): void => { const printCar = (car: Car): void => {
console.log(`${car.carModel} (${car.carColor})`) console.log(`${car.carModel} (${car.carColor})`)
} }
``` ```
@ -121,11 +121,11 @@ const printCar = (car: Car): void => {
```typescript ```typescript
interface Car { interface Car {
model: string model: string
color: "red" | "blue" | "yellow" color: "red" | "blue" | "yellow"
} }
const printCar = (car: Car): void => { const printCar = (car: Car): void => {
console.log(`${car.model} (${car.color})`) console.log(`${car.model} (${car.color})`)
} }
``` ```
@ -174,13 +174,13 @@ import fs from "node:fs"
import path from "node:path" import path from "node:path"
const createFile = async ( const createFile = async (
name: string, name: string,
isTemporary: boolean = false, isTemporary: boolean = false,
): Promise<void> => { ): Promise<void> => {
if (isTemporary) { if (isTemporary) {
return await fs.promises.writeFile(path.join("temporary", name), "") return await fs.promises.writeFile(path.join("temporary", name), "")
} }
return await fs.promises.writeFile(name, "") return await fs.promises.writeFile(name, "")
} }
``` ```
@ -193,11 +193,11 @@ import fs from "node:fs"
import path from "node:path" import path from "node:path"
const createFile = async (name: string): Promise<void> => { const createFile = async (name: string): Promise<void> => {
await fs.promises.writeFile(name, "") await fs.promises.writeFile(name, "")
} }
const createTemporaryFile = async (name: string): Promise<void> => { const createTemporaryFile = async (name: string): Promise<void> => {
await createFile(path.join("temporary", name)) await createFile(path.join("temporary", name))
} }
``` ```

View File

@ -40,8 +40,8 @@ These configurations are stored in the `.gitconfig` file in your home directory
```sh ```sh
[user] [user]
name = Username name = Username
email = email@example.com email = email@example.com
``` ```
You can find more information and useful `git` configurations in the [official documentation](https://git-scm.com/docs/git-config). You can find more information and useful `git` configurations in the [official documentation](https://git-scm.com/docs/git-config).
@ -88,6 +88,8 @@ git commit -m "Commit message"
# Commit changes in the past # Commit changes in the past
git commit --date "10 day ago" -m "Commit message" git commit --date "10 day ago" -m "Commit message"
# Also update the committer date for the last commit
git filter-branch --env-filter 'export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"' HEAD^..HEAD
# Add remote repository # Add remote repository
git remote add <remote> <url> git remote add <remote> <url>
@ -266,7 +268,7 @@ Sometimes, you want to compare what commits have been made between two branches,
```sh ```sh
[alias] [alias]
diff-commits = !sh -c 'echo -n "Commits in $2 not in $1 \\(" && printf "%d" $(git cherry -v $1 $2 | wc -l) && echo "\\)" && git cherry -v $1 $2 && echo "" && echo -n "Commits in $1 not in $2 \\(" && printf "%d" $(git cherry -v $2 $1 | wc -l) && echo "\\)" && git cherry -v $2 $1' - diff-commits = !sh -c 'echo -n "Commits in $2 not in $1 \\(" && printf "%d" $(git cherry -v $1 $2 | wc -l) && echo "\\)" && git cherry -v $1 $2 && echo "" && echo -n "Commits in $1 not in $2 \\(" && printf "%d" $(git cherry -v $2 $1 | wc -l) && echo "\\)" && git cherry -v $2 $1' -
``` ```
With this alias, we can compare the commits between `main` and `develop` branches for example: With this alias, we can compare the commits between `main` and `develop` branches for example:

View File

@ -39,23 +39,23 @@ The code of this website is open source on [GitHub](https://github.com/theoludwi
- [Next.js](https://nextjs.org/) - [Next.js](https://nextjs.org/)
It allows to have a server-side rendered website, that means that it is faster and easier to have a good <abbr title="Search Engine Optimization">SEO</abbr> than a <abbr title="Single Page Application">SPA</abbr>. It allows to have a server-side rendered website, that means that it is faster and easier to have a good <abbr title="Search Engine Optimization">SEO</abbr> than a <abbr title="Single Page Application">SPA</abbr>.
- [MDX](https://mdxjs.com/) - [MDX](https://mdxjs.com/)
MDX is an extension of Markdown that allows you to use custom React components. MDX is an extension of Markdown that allows you to use custom React components.
Here's what Markdown looks like: Here's what Markdown looks like:
```md ```md
A simple paragraph, with some **bold** text and some `inline code`. A simple paragraph, with some **bold** text and some `inline code`.
``` ```
When using Markdown in a web application, there's a "compile" step; the Markdown needs to be transformed into HTML, so that it can be understood by the browser. Those asterisks get turned into a `<strong>` tag, and each paragraph gets a `<p>` tag etc. When using Markdown in a web application, there's a "compile" step; the Markdown needs to be transformed into HTML, so that it can be understood by the browser. Those asterisks get turned into a `<strong>` tag, and each paragraph gets a `<p>` tag etc.
- [Tailwind CSS](https://tailwindcss.com/) - [Tailwind CSS](https://tailwindcss.com/)
Tailwind is a CSS framework to rapidly build modern websites without ever leaving HTML. Tailwind is a CSS framework to rapidly build modern websites without ever leaving HTML.
## Conclusion ## Conclusion

View File

@ -2,7 +2,6 @@ import sharedConfig from "@repo/config-tailwind"
/** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */ /** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */
const config = { const config = {
content: ["./src/**/*.tsx"],
presets: [sharedConfig], presets: [sharedConfig],
} }

View File

@ -1,10 +1,9 @@
import type fr from "./translations/fr-FR.json" import type { routing } from "./routing.ts"
import type messages from "./translations/en-US.json"
type Messages = typeof fr declare module "next-intl" {
interface AppConfig {
declare global { Locale: (typeof routing.locales)[number]
/** Messages: typeof messages
* Use type safe message keys with `next-intl`. }
*/
interface IntlMessages extends Messages {}
} }

View File

@ -1,15 +1,16 @@
import type { AbstractIntlMessages } from "next-intl" import type { AbstractIntlMessages } from "next-intl"
import { hasLocale } from "next-intl"
import { getRequestConfig } from "next-intl/server" import { getRequestConfig } from "next-intl/server"
import { routing } from "./routing.ts"
import type { Locale } from "@repo/utils/constants" import { LOCALE_DEFAULT } from "@repo/utils/constants"
import { LOCALE_DEFAULT, LOCALES } from "@repo/utils/constants"
import { deepMerge } from "@repo/utils/objects" import { deepMerge } from "@repo/utils/objects"
export default getRequestConfig(async ({ requestLocale }) => { export default getRequestConfig(async ({ requestLocale }) => {
let locale = await requestLocale const requested = await requestLocale
if (!LOCALES.includes(locale as Locale)) { const locale = hasLocale(routing.locales, requested)
locale = LOCALE_DEFAULT ? requested
} : routing.defaultLocale
const userMessages = (await import(`./translations/${locale}.json`)).default const userMessages = (await import(`./translations/${locale}.json`)).default
const defaultMessages = ( const defaultMessages = (

View File

@ -140,6 +140,10 @@
"title": "Open-Source" "title": "Open-Source"
}, },
"portfolio": { "portfolio": {
"fusey": {
"description": "ARK: Survival Ascended Wiki and Player stats tracker.",
"title": "Fusey"
},
"carolo": { "carolo": {
"description": "Strategy board game similar to chess which allows grandiose moves (only available in French).", "description": "Strategy board game similar to chess which allows grandiose moves (only available in French).",
"title": "Carolo" "title": "Carolo"

View File

@ -140,6 +140,10 @@
"title": "Open-Source" "title": "Open-Source"
}, },
"portfolio": { "portfolio": {
"fusey": {
"description": "ARK: Survival Ascended Wiki et suivi des statistiques des joueurs.",
"title": "Fusey"
},
"carolo": { "carolo": {
"description": "Jeu de plateau stratégique similaire aux échecs qui permet des coups grandioses, reposant sur des enchaînements remarquables.", "description": "Jeu de plateau stratégique similaire aux échecs qui permet des coups grandioses, reposant sur des enchaînements remarquables.",
"title": "Carolo" "title": "Carolo"

View File

@ -0,0 +1,5 @@
{
"plugins": {
"@tailwindcss/postcss": {}
}
}

View File

@ -51,6 +51,7 @@
"eslint": "catalog:", "eslint": "catalog:",
"postcss": "catalog:", "postcss": "catalog:",
"tailwindcss": "catalog:", "tailwindcss": "catalog:",
"@tailwindcss/postcss": "catalog:",
"typescript-eslint": "catalog:", "typescript-eslint": "catalog:",
"typescript": "catalog:" "typescript": "catalog:"
} }

View File

@ -1,7 +0,0 @@
const config = {
plugins: {
tailwindcss: {},
},
}
export default config

View File

@ -9,18 +9,25 @@ export const Portfolio: React.FC<PortfolioProps> = () => {
const t = useTranslations() const t = useTranslations()
const items: PortfolioProject[] = [ const items: PortfolioProject[] = [
{
id: "fusey",
title: t("home.portfolio.fusey.title"),
description: t("home.portfolio.fusey.description"),
link: "https://fusey.gg",
image: "/images/portfolio/Fusey.webp",
},
{ {
id: "carolo", id: "carolo",
title: t("home.portfolio.carolo.title"), title: t("home.portfolio.carolo.title"),
description: t("home.portfolio.carolo.description"), description: t("home.portfolio.carolo.description"),
link: "https://carolo.theoludwig.fr/", link: "https://carolo.theoludwig.fr",
image: "/images/portfolio/Carolo.webp", image: "/images/portfolio/Carolo.webp",
}, },
{ {
id: "leon", id: "leon",
title: t("home.portfolio.leon.title"), title: t("home.portfolio.leon.title"),
description: t("home.portfolio.leon.description"), description: t("home.portfolio.leon.description"),
link: "https://getleon.ai/", link: "https://getleon.ai",
image: "/images/portfolio/Leon.webp", image: "/images/portfolio/Leon.webp",
}, },
] ]

View File

@ -33,7 +33,7 @@ export const PortfolioItem: React.FC<PortfolioItemProps> = (props) => {
<div className="flex justify-center"> <div className="flex justify-center">
<Image <Image
quality={100} quality={100}
className="size-[300px] transition-opacity duration-500 group-hover:opacity-20 dark:group-hover:opacity-5" className="size-[300px] rounded-xl transition-opacity duration-500 group-hover:opacity-20 dark:group-hover:opacity-5"
width={300} width={300}
height={300} height={300}
src={image} src={image}

View File

@ -2,7 +2,6 @@
import { classNames } from "@repo/config-tailwind/classNames" import { classNames } from "@repo/config-tailwind/classNames"
import { usePathname, useRouter } from "@repo/i18n/routing" import { usePathname, useRouter } from "@repo/i18n/routing"
import type { Locale } from "@repo/utils/constants"
import { LOCALES } from "@repo/utils/constants" import { LOCALES } from "@repo/utils/constants"
import { useLocale } from "next-intl" import { useLocale } from "next-intl"
import { useEffect, useRef } from "react" import { useEffect, useRef } from "react"
@ -16,7 +15,7 @@ export interface LocalesProps {}
export const Locales: React.FC<LocalesProps> = () => { export const Locales: React.FC<LocalesProps> = () => {
const router = useRouter() const router = useRouter()
const pathname = usePathname() const pathname = usePathname()
const localeCurrent = useLocale() as Locale const localeCurrent = useLocale()
const { const {
value: isVisibleMenu, value: isVisibleMenu,

View File

@ -2,7 +2,6 @@ import sharedConfig from "@repo/config-tailwind"
/** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */ /** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */
const config = { const config = {
content: ["./src/**/*.tsx"],
presets: [sharedConfig], presets: [sharedConfig],
} }

7262
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -5,66 +5,63 @@ packages:
catalog: catalog:
# Turborepo # Turborepo
"turbo": "2.4.0" "turbo": "2.5.3"
# Utils # Utils
"mime": "4.0.6" "mime": "4.0.7"
# React.js/Next.js # React.js/Next.js
"next": &next "15.1.6" "next": &next "15.3.2"
"next-intl": "3.26.3" "next-intl": "4.1.0"
"next-themes": "0.4.4" "next-themes": "0.4.6"
"nuqs": "2.3.2" "react": "19.1.0"
"react": "19.0.0" "react-dom": "19.1.0"
"react-dom": "19.0.0" "react-icons": "5.5.0"
"react-icons": "5.4.0" "@types/react": "19.1.3"
"@types/react": "19.0.8" "@types/react-dom": "19.1.3"
"@types/react-dom": "19.0.3"
# Blog # Blog
"@giscus/react": "3.1.0" "@giscus/react": "3.1.0"
"gray-matter": "4.0.3" "gray-matter": "4.0.3"
"katex": "0.16.21" "katex": "0.16.22"
"next-mdx-remote": "5.0.0" "next-mdx-remote": "5.0.0"
"@mdx-js/mdx": "3.1.0" "@mdx-js/mdx": "3.1.0"
"rehype-katex": "7.0.1" "rehype-katex": "7.0.1"
"rehype-raw": "7.0.0" "rehype-raw": "7.0.0"
"rehype-slug": "6.0.0" "rehype-slug": "6.0.0"
"remark-gfm": "4.0.0" "remark-gfm": "4.0.1"
"remark-math": "6.0.0" "remark-math": "6.0.0"
"shiki": "1.24.0" "shiki": "1.24.0"
"@shikijs/rehype": "1.24.0" "@shikijs/rehype": "1.24.0"
# Markdown Lint # Markdown Lint
"markdownlint-cli2": "0.17.2" "markdownlint-cli2": "0.18.0"
"markdownlint": "0.37.4" "markdownlint": "0.38.0"
"markdownlint-rule-relative-links": "4.0.1" "markdownlint-rule-relative-links": "4.1.0"
# TypeScript # TypeScript
"typescript": "5.7.3" "typescript": "5.8.3"
"@total-typescript/ts-reset": "0.6.1" "@total-typescript/ts-reset": "0.6.1"
"@types/node": "22.13.1" "@types/node": "22.15.17"
# ESLint # ESLint
"globals": "15.14.0" "globals": "16.1.0"
"typescript-eslint": "8.23.0" "typescript-eslint": "8.32.0"
"@eslint/eslintrc": "3.2.0" "@eslint/eslintrc": "3.3.1"
"eslint": "9.20.0" "eslint": "9.26.0"
"eslint-config-conventions": "18.0.2" "eslint-config-conventions": "19.2.0"
"eslint-plugin-promise": "7.2.1" "eslint-plugin-promise": "7.2.1"
"eslint-plugin-unicorn": "56.0.1" "eslint-plugin-unicorn": "59.0.1"
"eslint-config-next": *next "eslint-config-next": *next
"eslint-plugin-storybook": "0.11.2" "eslint-plugin-import-x": "4.11.1"
"eslint-plugin-tailwindcss": "3.18.0"
"eslint-plugin-import-x": "4.6.1"
# Prettier # Prettier
"prettier": "3.4.2" "prettier": "3.5.3"
"prettier-plugin-tailwindcss": "0.6.11" "prettier-plugin-tailwindcss": "0.6.11"
"editorconfig-checker": "6.0.1" "editorconfig-checker": "6.0.1"
# Storybook # Storybook
"storybook": &storybook "8.5.3" "storybook": &storybook "8.6.12"
"@storybook/addon-essentials": *storybook "@storybook/addon-essentials": *storybook
"@storybook/addon-storysource": *storybook "@storybook/addon-storysource": *storybook
"@storybook/addon-a11y": *storybook "@storybook/addon-a11y": *storybook
@ -74,22 +71,35 @@ catalog:
"@storybook/react": *storybook "@storybook/react": *storybook
"@storybook/test": *storybook "@storybook/test": *storybook
"@storybook/addon-themes": *storybook "@storybook/addon-themes": *storybook
"@storybook/test-runner": "0.21.0" "@storybook/test-runner": "0.22.0"
"@chromatic-com/storybook": "3.2.4" "@chromatic-com/storybook": "3.2.6"
"chromatic": "11.25.2" "chromatic": "11.28.2"
"storybook-dark-mode": "4.0.2" "storybook-dark-mode": "4.0.2"
# Testing # Testing
"playwright": &playwright "1.50.1" "playwright": &playwright "1.52.0"
"@playwright/test": *playwright "@playwright/test": *playwright
"axe-playwright": "2.1.0" "axe-playwright": "2.1.0"
"start-server-and-test": "2.0.10" "start-server-and-test": "2.0.11"
# CSS # CSS
"postcss": "8.5.1" "postcss": "8.5.3"
"tailwindcss": "3.4.17" "@tailwindcss/postcss": "4.1.6"
"@tailwindcss/typography": "0.5.15" "@tailwindcss/typography": "0.5.16"
"tailwind-merge": "2.6.0" "tailwindcss": "4.1.6"
"tailwind-merge": "3.2.0"
"clsx": "2.1.1" "clsx": "2.1.1"
"cva": "1.0.0-beta.3" "cva": "1.0.0-beta.3"
"@fontsource/montserrat": "5.1.1" "@fontsource/montserrat": "5.2.5"
onlyBuiltDependencies:
- "@swc/core"
- "@tailwindcss/oxide"
- "core-js-pure"
- "esbuild"
- "sharp"
- "unrs-resolver"
publicHoistPattern:
- "*eslint*"
- "*prettier*"