diff --git a/.gitignore b/.gitignore index d63aab048..6240da8b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,21 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies node_modules/ -public -.gatsby-context.js + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files .DS_Store -.intermediate-representation/ -.cache/ -.yalc -.vscode \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 64cb3e63c..bec176669 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,16 +1,19 @@ { - arrowParens: 'avoid', - endOfLine: 'lf', - printWidth: 80, - proseWrap: 'always', - semi: false, - singleQuote: true, - tabWidth: 2, - trailingComma: 'es5', - overrides: [{ - files: ['*.md', '*.mdx'], - options: { - printWidth: 100 + "arrowParens": "avoid", + "endOfLine": "lf", + "printWidth": 80, + "proseWrap": "always", + "semi": false, + "singleQuote": true, + "useTabs": false, + + "plugins": ["prettier-plugin-astro"], + "overrides": [ + { + "files": ["*.md", "*.mdx"], + "options": { + "printWidth": 100 + } } - }] + ] } diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..d64220976 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..fcdf8566d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "prettier.documentSelectors": ["**/*.astro"], + "tailwindCSS.classAttributes": ["class", "class:list", "className", "ngClass"] +} diff --git a/LICENSE b/LICENSE index cd1e777f7..4840d3a9d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2020 Maxence Poutord +Copyright (c) 2014-2024 Maxence Poutord Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 000000000..eff4391fb --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,24 @@ +import { defineConfig } from 'astro/config' +import mdx from '@astrojs/mdx' +import sitemap from '@astrojs/sitemap' +import preact from '@astrojs/preact' +import tailwind from '@astrojs/tailwind' + +import { SITE_URL } from './src/consts' +import { getLegacyPostRedirections } from './src/utils/301' + +// https://astro.build/config +export default defineConfig({ + site: SITE_URL, + markdown: { + syntaxHighlight: 'prism', + shikiConfig: { + theme: 'one-dark-pro', + }, + }, + redirects: { + '/blog/pages/1': '/blog', + ...getLegacyPostRedirections(), + }, + integrations: [mdx(), sitemap(), tailwind(), preact()], +}) diff --git a/content/_drafts/2020-code-review/code-review.md b/content/_drafts/2020-code-review/code-review.md deleted file mode 100644 index 61c52d409..000000000 --- a/content/_drafts/2020-code-review/code-review.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: 'Code Reviews made it Healthy' -slug: code-review-guidelines -language: en -date: 2020-10-01 -cover: ./cover.jpeg -tags: - - Code Quality ---- - -## Benefits of PR - -- Discuss about the code: I have a problem, this is how I solved it; -- Spot potentials bugs; -- Discuss about the architecture. - -And also be a better developer. **Reading code is probably the best way to improve your coding -skills**! - -## Definition of Done - -**Author** - -- 1 issue, 1 PR. -- PR MUST be marked green by CI. -- 2 approvals before merging. - - more reviewers don't necessarily mean fewer defects - - more reviewers spend less time -- UI/UX changes? add screenshots/capture. Need a tool for video recording? Use - [Giphy](https://itunes.apple.com/us/app/giphy-capture-the-gif-maker/id668208984)! -- Your PR is described and follow the template. - -**Reviewer** - -- You don't see apparent bugs/regression; -- You understand the problem and how the author solved it; -- You fully understand the code and the underlying changes (you can maintain the code); -- No dead code or TODOs; -- Code is consistent with the rest of the application; -- Code is tested and documented. - -If everything looks good to you, thumbed up/approve the Pull Request. - -Have some doubt? `git pull` the code and test it locally! (If so, you can add a message to prevent -merge when you're testing it). - -## Flow - -1. Write code... -2. Open a pull request and assign it to yourself -3. Review your own code on Gitlab. Try to spot `console.debug`, `// dead code`... -4. Remove the WIP status. Make sure you filled the description field / the pull request template -5. Post it on the #codereviewrequests Slack channel -6. Get your 2 thumbs up and resolve conversation -7. Merge it - -_Feel free to invert 1 and 2. Opening a PR before writing code is a good way to show your colleagues -what you're working on!_ - -## Best practices - -### For everybody - -- Code review should be about code and only the code. -- Your opinion is not necessary everybody's opinion. -- PR are not a place to shine / show off with your coding skills. -- Final editorial control rests with the pull request author. -- Avoid ownership words ("mine", "not mine", "yours"). Once the code is merged, it's everybody's - code. Not yours. -- Avoid statements words like "obviously", "everyone knows", "just"... something "simple" for you is - not necessarily that simple for someone else. -- Favour one-to-one conversation. -- [Respectful Reviews === Useful Reviews](https://testing.googleblog.com/2019/11/code-health-respectful-reviews-useful.html) -- You're not on a Reddit forum. Move philosophical/academic conversation to the weekly meeting ...or - not! -- You are not your code. -- If you find something you like, **say it** πŸ‘ - -### For the author - -- Keep your PR as small as posible. - > ask a programmer to review 10 lines of codes, they'll fill 10 issues ask them to review 500 - > lines of code, they will say it looks good. -- Big change? communicate! If your PR includes big change (framework version...), the team must be - aware! -- (Try to) respond to every comment. -- Don't do sneaky updates. Don't take leverage of a big pull request to force or hide a change. - Same, don't add big changes after getting approvals! It's unfair! -- Provide context. It's ok to copy/paste code from somewhere, we all do. If you did or if you - followed random tutorial, add the link to the pull request (or in a comment in the code). -- Keep the git log clean. - - 1 commit for the requested changes. Reviewers don't want to go through 20 commits to see changes - you did - - `git rebase` and `git commit --amend --no-edit` are your friends - - If your PR got several commits, consider squashing (squash and merge) - -### For the reviewer - -- Don't do someone else's job. - - "You forget a semicolon" => Stop. It's worthless because it's a robot job, not a human job. - - "Tests are failing" => Same. We have robots for that. -- Prefix _nitpicky comments_ with "nit" or "nitpicking". -- The code review's author is not a piΓ±ata. So, don't be a jerk. - - Avoid: "your code is unreadable". - - Prefer proposing alternative implementations "You can also split this code into 2 different - functions. What do you think about?" (assume the author already considered them) -- Ask for clarification. ("I didn't understand xxx. Can you clarify?"). Do it, even if the PR had - already been approved by someone else. -- Remember that you are here to provide feedback, not to be a gatekeeper. -- Remember that **behind each Pull Request, there's a human who spent time to write this code** (the - size of the PR is not necessarily correlated to the time spent on it). -- Done is better than perfect. - -## List of valid reasons to πŸ‘Ž a PR - -- (none) - -Declining a PR can be really harsh for the author. It should be the very last call. Favor 1-to-1 -conversation instead and let the author decline his own PR or do the changes. - -If the pull request bring something you consider harmful for the project, you can add an "🚨" emoji -to prevent the merge. - -## Tips for better code review - -- To remove noise, you can use the "hide whitespace" option. -- If your PR is not ready, mark it as _in progress_ (prefix it with `WIP:` in GitLab). Don't waste - someone else time! -- Asking junior to review your code first. If a senior developper review the PR first, the junior - will be less eager to spot issues. - -_Inspired by -[thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review)._ diff --git a/content/_drafts/2020-code-review/cover.jpeg b/content/_drafts/2020-code-review/cover.jpeg deleted file mode 100644 index 414a74378..000000000 Binary files a/content/_drafts/2020-code-review/cover.jpeg and /dev/null differ diff --git a/content/_drafts/2022-05-08-git-log-next-level/basic.png b/content/_drafts/2022-05-08-git-log-next-level/basic.png deleted file mode 100644 index 699dc613a..000000000 Binary files a/content/_drafts/2022-05-08-git-log-next-level/basic.png and /dev/null differ diff --git a/content/_drafts/2022-05-08-git-log-next-level/cover.jpg b/content/_drafts/2022-05-08-git-log-next-level/cover.jpg deleted file mode 100644 index 26c5180fc..000000000 Binary files a/content/_drafts/2022-05-08-git-log-next-level/cover.jpg and /dev/null differ diff --git a/content/_drafts/2022-05-08-git-log-next-level/git-log-advanced.png b/content/_drafts/2022-05-08-git-log-next-level/git-log-advanced.png deleted file mode 100644 index 37ccf5de0..000000000 Binary files a/content/_drafts/2022-05-08-git-log-next-level/git-log-advanced.png and /dev/null differ diff --git a/content/_drafts/2022-05-08-git-log-next-level/git-log-with-files.png b/content/_drafts/2022-05-08-git-log-next-level/git-log-with-files.png deleted file mode 100644 index 5729aaa6a..000000000 Binary files a/content/_drafts/2022-05-08-git-log-next-level/git-log-with-files.png and /dev/null differ diff --git a/content/_drafts/2022-05-08-git-log-next-level/index.mdx b/content/_drafts/2022-05-08-git-log-next-level/index.mdx deleted file mode 100644 index 80818e76e..000000000 --- a/content/_drafts/2022-05-08-git-log-next-level/index.mdx +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: 'Bringing git log to the next level' -slug: git-log-next-level -language: en -date: 2022-05-08 -cover: ./cover.jpg -tags: ['Git'] ---- - -After speaking with some colleagues, I was very surprised to see how many of them use GUI tools for -git. - -I used to list my commits with `git log --oneline --decorate --graph` - -## The basic: oneline + decorate + graph - -```bash -git log --oneline --decorate --graph -# aliased by -alias glog='git log --oneline --decorate --graph' -``` - -![git log --oneline --decorate --graph](./basic.png) - -Small and consise: everything I like. I found this command perfect until I found the next one! - -## glol: git log on steroids - -```bash -glol -# aliased by -alias glol='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\' -``` - -![git log --oneline --decorate --graph](./git-log-advanced.png) - -## glols: the same but with diff included - -glols = Git log + ls - -```bash -glols -# aliased by -glols='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'' --stat' -``` - -![](./git-log-with-files.png) - -If you use oh-my-zsh, these commands should be here by default diff --git a/content/_drafts/JS-snippets/index.mdx b/content/_drafts/JS-snippets/index.mdx deleted file mode 100644 index eb01e18cf..000000000 --- a/content/_drafts/JS-snippets/index.mdx +++ /dev/null @@ -1,40 +0,0 @@ -## Debug in 2s - -This is particulary interesting for debugging dropdrown/popup, or any temporary HTML element you -struggle to have focus on. - -```js -setTimeout(function () { - debugger -}, 2000) -``` - -## prevent page to reload - -```js -window.addEventListener( - 'beforeunload', - () => { - debugger - }, - false -) -``` - -## debug all colors - -https://github.com/bgrins/devtools-snippets/blob/master/snippets/allcolors/allcolors.js - -## [A11y] - the 44x44 cursor - -```js -;(function () { - const style = document.createElement('style'), - styleContent = document.createTextNode(`* { - cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJBJREFUeNrs2dEKgDAIhWEN3/+VLYtBF12MppHwDxY0uvg4DJpORcSl0bB4uPcwq+oFHu8/956pbtJsAAYMGDBgwIABA044t3pMEgb8VU2XsWdn1o/aUUn4bchPBWNFYWvVidxbCFGmr7YULBs1YDPfsYcXEi9ryvDjAAwYMGDAgAED7nK8bHG7qNLs6nYXYAAheh5j8Qw5fwAAAABJRU5ErkJggg==) 22 22, auto !important - }`) - style.appendChild(styleContent) - const caput = document.getElementsByTagName('head') - caput[0].appendChild(style) -})() -``` diff --git a/content/_drafts/architectural-decision-template/index.md b/content/_drafts/architectural-decision-template/index.md deleted file mode 100644 index 8ca7196a9..000000000 --- a/content/_drafts/architectural-decision-template/index.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: 'My Architectural Decision Template' -slug: architectural-decision -language: en -date: 2042-01-01 -tags: - - Architecture ---- - -In my career, I took various architectural decisions. And often, these decisions had an impact on -the project's life. - -Writing an Architectural Decision Record might look a bit academic or corporative but, I believe it -good to document the decision. It also helps future developers to understand the decision. - -## Template - -```markdown -# Title - -Status: Proposed | Accepted | Deprecated Decision: which solution - -## Burning platform - -Why are you here today thinking about a different architecture/drastic change. -Try to be as factual as possible. Feel free to provide metrics. - -## Propositions - -### Proposition 1 - -_Small description of what is this solution about._ - -- list positive/negative consequences -- pros&cons - -### Proposition 2 - -_Small description of what is this solution about._ - -- list positive/negative consequences -- pros&cons - -## (Optional) recap - -(insert a comparative table where you compare propositions) -Example of elements to compare: effort, learning curve, available market skills... - -## (Optional) Aditional resources - -links -``` - -**Notes:** - -- I like to put the actual system as the 1st proposition. -- Be honest. In this list, you probably have a favorite proposition. If your solution appears to be - a silver bullet, you will lose all credibility. -- You can also put a link to a POC. But keep in mind that, a freshly written hello-world will most - of the time looks better than a few-years-old living codebase. Also beaware of hello world driven - decision. -- if the - -## Focus #1: go straight to the point - -This document is not intended to be published and sold in a bookstore. Feel free to abuse from -bullet list (but don't go too crazy!). Also, keep in mind that more words means more room for -misinterpretation. - -The document should not exceed 1 or 2 pages. - -## Focus #2: Banwords - -- Words like "simple", "just", "of course", "everyone knows", "easy" don't add any value. In fact, - it often does the opposite. Something simple is not necessarily simple for everybody; -- pejorative words. Respect your elders! Also, keep in mind that the decision-maker might be - involved in the previous system. You want him as an ally not an ennemy. Also, when something is - bad, it's something know diff --git a/content/_drafts/design-system/cover.jpeg b/content/_drafts/design-system/cover.jpeg deleted file mode 100644 index 31a0fb91c..000000000 Binary files a/content/_drafts/design-system/cover.jpeg and /dev/null differ diff --git a/content/_drafts/design-system/github-in-2013.png b/content/_drafts/design-system/github-in-2013.png deleted file mode 100644 index fedeaa99c..000000000 Binary files a/content/_drafts/design-system/github-in-2013.png and /dev/null differ diff --git a/content/_drafts/design-system/index.mdx b/content/_drafts/design-system/index.mdx deleted file mode 100644 index 7561830fc..000000000 --- a/content/_drafts/design-system/index.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: You should Probably Not Create your Component Library from Scratch -slug: component-library-from-scratch -language: en -date: 2022-01-01 -cover: ./cover.jpeg ---- - -Earlier this year, I wrote a post where I mentioned how inefficient it is to build your own design -system in 2021. As expected, it gave some people had knee-jerk reaction. - -## "It ends up with an unpolished look and feel" - -> "I love this website, buttons have the perfect border-radius." -> Nobody. - -I'm making app for +10y now and, I'm still surprised to see how many companies have their own -implementation of a god damn button. Back in the days, developpers were crazy about creating their -PHP framework. In 2021, we're still reinventing the wheel with our UI. - -Let's be honest. Your application UI is not that specific. Whatever our ego would love to think, an -application is not a piece of art. User don't go there just for to see the beauty of what CSS can -offer. People use the app because it fixes problems. - -Check this -[2013 archive of GitHub.com](https://web.archive.org/web/20130817235413/https://github.com/twbs/bootstrap). -Don't you recognize anything? - -![Github in 2013](./github-in-2013.png) - -Bootstrap 2.0. Everywhere! And their app was: making tons of money, loved by many developers, ranked -in the top 100 of the most visited websites. Oh, and they also got acquired by Microsoft for $7.5 -billion. - -Instead of satisfying our ego we were focusing more on the product? - -## Your custom design system is probably not accessible - -Accessibility (a11y) is complicated. I understand that buttons components can be straightforward to -implement. It's another story when it comes to modals, dropdowns, alerts... Keyboard navigation, -focus, component's state are more complex. And not everyone is aware of some screen readers and how -to use them. - -By the way, if you never used a screen reader, I bet your components are not accessible. - -If your design system is built on the top of a battle-tested library, you can't be sure that it's -accessible. - -## "Yeah but, libraries have limitations!" - -In my actual company we also had this discussion about design system. When I suggested to build our -own design system on the top of an open-source component library, a developer mentioned that these -libraries may have limitations. Which is a fair point. - -Again, you sure your application is that unique? Also, ask yourself: _"Can I live with this missing -feature or not? Is there is an alternative component? What is the impact on the business side?"_. Is -this missing feature is important enough to invest days of design&developement? (with all hidden -costs like testing, maintenance & documentation). - -If you're in a company that employs many developers, why not. But if you're in a startups/mid-sized -companies, I don't think you should. - -## Personnal Reccomendations - -[Reach UI](https://reach.tech) -[Material UI](https://mui.com) -[Tailwind UI](https://tailwindui.com) -[RSuite](https://rsuitejs.com) -[React Spectrum](https://react-spectrum.adobe.com/react-spectrum/index.html) diff --git a/content/_drafts/rethinking-cypress-tests/index.mdx b/content/_drafts/rethinking-cypress-tests/index.mdx deleted file mode 100644 index 4f943cdff..000000000 --- a/content/_drafts/rethinking-cypress-tests/index.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: 'Pimping my Cypress.js tests' -slug: rethinking-my-cypress-tests -language: en -date: 2022-12-22 -# cover: ./cover.jpg -tags: ['Testing', 'JavaScript'] ---- - -A few years back, I thought e2e tests they were slow and expensive... I think it a relic of an old -concept call [testing-pyramid](https://martinfowler.com/articles/practical-test-pyramid.html), I -learn back in the days. Fortunatelly, writting e2e test in 2022 is faster than 20years ago when this -concept was created. Mostly with Cypress. - -Cypress is probably one of the most popular e2e testing framework. I have been using it for over a -year now - -It's fast and feel confidend to see the result in a real browser. - -Let say I have a `login.spec.js` file, - -```js -it('should login', () => { - cy.visit('/login') - cy.get('[data-testid=login-username]').type('maxence') - cy.get('[data-testid=login-password]').type('mycoolpassword') - cy.get('[data-testid=login-login-btn]').click() -}) -``` - -## Testing-library + Cypress = ❀️ - -When I started using it with testing-library, it was even better. - -```js -it('should login', () => { - cy.visit('/login') - cy.findByPlaceholderText('Username').type('maxence') - cy.findByPlaceholderText('password').type('mycoolpassword') - cy.findByRole('button', { name: /Login/i }).click() -}) -``` - -## Abstracting diff --git a/content/_drafts/the-needle-and-the-hammer.md b/content/_drafts/the-needle-and-the-hammer.md deleted file mode 100644 index 465aad1ea..000000000 --- a/content/_drafts/the-needle-and-the-hammer.md +++ /dev/null @@ -1,20 +0,0 @@ -# The Needle and the Hammer - -## "But, you never worked with React before?" - -Would you work with a plumber that only works with a screwdriver? Even if this guy told you "I'm the -master of screwdrivers, I nailed this skill over the past year". - -No, you wouldn't. But, you hire developpers that only knows React. - -## "We need RxJS/Redux/XState/TS/..." - -> Dev1: How should I handle my 3 steps form? -> Dev2: This is a state machine problem. You can use Xstate to solve this problems. - -> Dev1: We should use TypeScript so Backend and Frontend can share model. If the Backend model -> change, the Frontend model will not break. That will fix our problem. -> Me: How many issues related to BE data model did we had over the past 6 months? -> Dev1: One. - -Because of our ego, we love to make overcomplicated things. Focus on the right things. diff --git a/content/_drafts/webmentions/disqus.png b/content/_drafts/webmentions/disqus.png deleted file mode 100644 index d08cac339..000000000 Binary files a/content/_drafts/webmentions/disqus.png and /dev/null differ diff --git a/content/_drafts/webmentions/index.md b/content/_drafts/webmentions/index.md deleted file mode 100644 index 68d3887b1..000000000 --- a/content/_drafts/webmentions/index.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: 'Webmentions' -slug: webmentions -language: en -date: 2022-12-22 -# cover: ./cover.jpg -tags: - - Gatsby ---- - -https://webmention.io/api/count.json?target=https://www.maxpou.fr/nomad-in-lisbon/ -https://webmention.io/api/mentions.jf2?per-page=20&target=https://www.maxpou.fr/nomad-in-lisbon/ -https://webmention.io/api/mentions.jf2?&wm-property=like-of&target=https://www.maxpou.fr/nomad-in-lisbon/ - -https://webmention.io/api/mentions?page=0&per-page=20&target=https://www.maxpou.fr/vue-js-testing-library/ -https://webmention.io/api/mentions?target=https://www.maxpou.fr/lesson-learned-maintaining-large-vue-js-codebase/ - -https://webmention.io/api/count?target=https://www.maxpou.fr/lesson-learned-maintaining-large-vue-js-codebase/ - ---> https://brid.gy/ diff --git a/content/images/avatar.jpeg b/content/images/avatar.jpeg deleted file mode 100644 index 559df92ab..000000000 Binary files a/content/images/avatar.jpeg and /dev/null differ diff --git a/content/images/icon.jpeg b/content/images/icon.jpeg deleted file mode 100644 index 559df92ab..000000000 Binary files a/content/images/icon.jpeg and /dev/null differ diff --git a/content/pages/about/helloworld.jpg b/content/pages/about/helloworld.jpg deleted file mode 100644 index eda2cbe49..000000000 Binary files a/content/pages/about/helloworld.jpg and /dev/null differ diff --git a/content/pages/rss/index.mdx b/content/pages/rss/index.mdx deleted file mode 100644 index be12aa912..000000000 --- a/content/pages/rss/index.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: RSS Feeds -slug: rss -webmentions: false ---- - -3 RSS feeds are available: - -- [maxpou.fr/rss-en.xml](/rss-en.xml): for posts in English only πŸ‡¬πŸ‡§ - - - follow me in feedly - -- [maxpou.fr/rss-fr.xml](/rss-fr.xml): for posts in French only πŸ‡«πŸ‡· - - - follow me in feedly - -- [maxpou.fr/rss.xml](/rss.xml): for posts in English and French - - - follow me in feedly - diff --git a/content/pages/speaking/MyTalks.js b/content/pages/speaking/MyTalks.js deleted file mode 100644 index 3c1881dad..000000000 --- a/content/pages/speaking/MyTalks.js +++ /dev/null @@ -1,220 +0,0 @@ -import React from 'react' -import { - List, - Link, - Item, - Event, - Media, -} from '../../../src/components/TalkList' - -const MyTalks = () => { - return ( - <> -

Talks

- - - - πŸ‡¬πŸ‡§ Web Accessibility 101 - - πŸ—“ 09/2021, Orderfox, ZΓΌrich, Switzerland - πŸ—“ 09/2020, Visma talk, Remote - - - - πŸ‡¬πŸ‡§ Effective Frontend testing - - πŸ—“ 09/2020, Vue.js Kenya, Remote - πŸ—“ 11/2018, MadridJS, Madrid, Spain - πŸ—“ 10/2018, VSware, Dublin, Ireland - - - - πŸ‡¬πŸ‡§ Progressive Web Apps: Your web app on steroids - - πŸ—“ 09/2018, DublinJS, Dublin, Ireland - - πŸ—“ 06/2018, Devoxx, KrakΓ³w, Poland β€”{' '} - - πŸ‡¬πŸ‡§ video (YouTube) - - - πŸ—“ 05/2018, VSware, Dublin, Ireland - - πŸ—“ 03/2018, Voxxed Days, Vienna, Austria β€”{' '} - - πŸ‡¬πŸ‡§ video (YouTube) - - - - - - πŸ‡¬πŸ‡§ Vue.js - Yet another JavaScript Framework (company conference) - - - πŸ—“ 12/2017, Hostelworld Technology Submit, Dublin, Ireland - - - - - πŸ‡¬πŸ‡§ Immutability for functional JavaScript - - πŸ—“ 09/2017, DublinJS, Dublin, Ireland - - - - πŸ‡¬πŸ‡§ Github Tips&Tricks - - - πŸ—“ 02/2017, Open Source Meetup, Dublin, Ireland β€”{' '} - - πŸ‡¬πŸ‡§ blog post - - - - - - πŸ‡¬πŸ‡§ Make your code great again with the Object Calisthenics - - - πŸ—“ 06/2017, Web2Day, Nantes, France β€”{' '} - - πŸ‡«πŸ‡· video (YouTube) - - - - πŸ—“ 05/2017, PHPTour, Nantes, France β€”{' '} - - πŸ‡«πŸ‡· video (YouTube) - - - πŸ—“ 02/2017, Dublin's PHP meetup, Dublin, Ireland - πŸ—“ 11/2016, Hostelworld.com, Dublin, Ireland - - - - πŸ‡¬πŸ‡§ GraphQL/Falcor: Why killing REST? - - πŸ—“ 04/2016, Human Talk, Nantes, France - - - - πŸ‡¬πŸ‡§ REST & Symfony - - - πŸ—“ 02/2016, SfPot, Nantes, France β€”{' '} - - πŸ‡«πŸ‡· video (YouTube) - - - πŸ—“ 02/2016, Conserto, Nantes, France - - - -

Trainings & Workshops

- - - - πŸ‡¬πŸ‡§ Vue.js Workshop - Build a StackOverflow app using Vue.js - - πŸ—“ 02/2018, DublinJS, Dublin, Ireland - - - - πŸ‡¬πŸ‡§ Vue.js Training - - - πŸ‡¬πŸ‡§ Vue.js Workshop - - - {' '} - β€” Training for{' '} - Hostelworld - - - - - πŸ‡¬πŸ‡§ Symfony2 Basics - {' '} - β€”{' '} - - PHP training for Conserto{' '} - & La Poste - - - - - πŸ‡¬πŸ‡§ PHP Basics - {' '} - β€”{' '} - - PHP training for Conserto - - - - -

Others presentations

- - - - πŸ‡¬πŸ‡§ Package management with NPM - {' '} - β€” Presentation for Orderfox (03/2022) - - - - πŸ‡¬πŸ‡§ An introduction to GraphQL - {' '} - β€” Presentation for Orderfox (11/2021) - - - - πŸ‡¬πŸ‡§ JS Today - a (re-)introduction to JavaScript - {' '} - β€”{' '} - - Presentation for{' '} - Hostelworld (06/2017) - - - - - πŸ‡«πŸ‡· Redis et l'Γ©cosysteme Symfony - {' '} - β€”{' '} - - Presentation for La Poste{' '} - (03/2016) β€”{' '} - - πŸ‡«πŸ‡· blog post - - - - - - πŸ‡«πŸ‡· PrΓ©sentation d'Elasticsearch - {' '} - β€”{' '} - - Presentation for La Poste{' '} - (03/2016) - - - - πŸ‡«πŸ‡· Pourquoi utiliser un framework comme Symfony2{' '} - β€”{' '} - - Presentation for Conserto{' '} - clients - - - - - πŸ‡«πŸ‡· Dissertation defence (Software Architect degree) - - - - - ) -} - -export default MyTalks diff --git a/content/posts/2019/2019-01-22-2018-a-year-in-review/cover.jpg b/content/posts/2019/2019-01-22-2018-a-year-in-review/cover.jpg deleted file mode 100644 index ec1cfa9c3..000000000 Binary files a/content/posts/2019/2019-01-22-2018-a-year-in-review/cover.jpg and /dev/null differ diff --git a/content/recipes/Creamy-mushroom-eggplants-49deb2b6e12747209f62431e9442e29a.mdx b/content/recipes/Creamy-mushroom-eggplants-49deb2b6e12747209f62431e9442e29a.mdx deleted file mode 100644 index 8458363e8..000000000 --- a/content/recipes/Creamy-mushroom-eggplants-49deb2b6e12747209f62431e9442e29a.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: Creamy mushroom&eggplants -date: 2023-01-10T18:40:00.000Z -slug: Creamy-mushroom-eggplants-49deb2b6e12747209f62431e9442e29a ---- - -![Creamy-mushroom-eggplants-49deb2b6e12747209f62431e9442e29a-0.jpg](./assets/Creamy-mushroom-eggplants-49deb2b6e12747209f62431e9442e29a-0.jpg) - -## Ingredients - -- Mushrooms -- Yellow pepper -- Spinaches -- 1/2 or small Eggplant -- 2 cloves of garlic -- 1 onion -- Parsley -- Cream (creme fraiche or soy cream) -- Salt, pepper - -## Steps - -1. in a pan, put the garlic and then the onion (3 minutes) -2. then add aubergine (5 minutes) -3. then add pepper (3 minutes) -4. add the rest until it’s fully cooked - -Can be served with rice or pasta. diff --git a/content/recipes/Hulk-Burger-broccoli-patties-7d2197f460e84034abd5faef83a8e8e4.mdx b/content/recipes/Hulk-Burger-broccoli-patties-7d2197f460e84034abd5faef83a8e8e4.mdx deleted file mode 100644 index 2b0c80170..000000000 --- a/content/recipes/Hulk-Burger-broccoli-patties-7d2197f460e84034abd5faef83a8e8e4.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Hulk Burger (broccoli patties) -date: 2023-06-29T13:30:00.000Z -slug: Hulk-Burger-broccoli-patties-7d2197f460e84034abd5faef83a8e8e4 ---- - -## Ingredients: (4 persons) - -- 500g of broccoli -- 2 slices of soft bread -- 1 egg -- 100g of dry mozzarella -- 1 shallot -- 1 clove of garlic -- cayenne pepper -- salt, pepper - -## Steps - -1. Roughly cut the broccoli (keep the small trunks but remove the big ones). -2. Put the broccoli in a pot of salted water. Boil for 10mins. Then plunge them in ice water to stop - the vegetables from cooking. Drying the broccoli with a tea towel. -3. Toast the bread. -4. Mince the shallot and the garlic. -5. Put all the ingredients in a food processor and mix everything. You want to keep some texture, - don’t pulse too much. -6. Shape 4 burgers (you can use ring moulds). -7. Cook slowly (we want the cheese to be melted!). - -## Variants - -- 200g of quinoa diff --git a/content/recipes/Lemon-Spaghetti-Shrimps-dde4d8fb979b4606b9e3624edb38552f.mdx b/content/recipes/Lemon-Spaghetti-Shrimps-dde4d8fb979b4606b9e3624edb38552f.mdx deleted file mode 100644 index 063deb4b0..000000000 --- a/content/recipes/Lemon-Spaghetti-Shrimps-dde4d8fb979b4606b9e3624edb38552f.mdx +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Lemon Spaghetti (+ Shrimps) -date: 2023-01-10T18:40:00.000Z -slug: Lemon-Spaghetti-Shrimps-dde4d8fb979b4606b9e3624edb38552f ---- - -## The shrimps (optional) - -### Ingredients: - -- ~10 shrimps -- 1 lemon juice -- Olive oil -- salt+pepper -- fresh leave of basil (if you have) - -### Steps: - -1. prepare a marinade with lemon, olive oil, and basil. Salt+pepper. -2. marinade for a minimum of 1 hour (up to 8) -3. saute for a ~3 minutes - -## Lemon Spaghetti - -### Ingredients: - -- spaghetti (or linguini) -- 1 lemon (organic) -- 4-5 garlic cloves -- cream -- salt+pepper -- parsley - -### Steps: - -1. prepare a pan of water. Once it’s boiling, add the pasta. -2. slice the garlic (no need to chop it!), -3. get the zest of the lemon and slice it into julienne/matchstick -4. on a hot pan, and put some butter with some olive oil (so the butter does not burn). When the - butter is melted, add the garlic. -5. after 1 minute (garlic shouldn’t be brown) add the lemon zest and the juice of 1/2 lemon -6. after 1 minute add the cream, salt+pepper and some parsley -7. and simmer until the spaghettis are cooked. -8. when the spaghetti are ready, don’t drain them too much and put them directly into the creamy - pan. -9. dress the pasta (with the shrimp). You can also add some parsley + a few lemon juliennes on top - -_inspired from this video:_ -[_https://www.youtube.com/watch?v=i84Sc5uvQa8_](https://www.youtube.com/watch?v=i84Sc5uvQa8) diff --git a/data/siteConfig.js b/data/siteConfig.js deleted file mode 100644 index 853da32fe..000000000 --- a/data/siteConfig.js +++ /dev/null @@ -1,107 +0,0 @@ -module.exports = { - siteTitle: 'Maxence Poutord', - siteDescription: 'Software engineer β€’ Public speaker β€’ Nomadic worker', - authorName: 'Maxence Poutord', - twitterUsername: '_maxpou', - authorAvatar: 'avatar.jpeg', - defaultLang: 'en', // show flag if lang is not default. Leave empty to enable flags in post lists - authorDescription: ` - Hey, I'm Maxence Poutord, a passionate software engineer. - In my day-to-day job, I'm working as a senior front-end engineer at Orderfox. - When I'm not working, you can find me travelling the world or cooking. - `, - seoDescription: - 'Personal site for Maxence Poutord, a software engineer who is specialised in Frontend development.', - siteUrl: 'https://www.maxpou.fr/', - // Prefixes all links. For cases when deployed to maxpou.fr/gatsby-starter-morning-dew/ - pathPrefix: '/', // Note: it must *not* have a trailing slash. - siteCover: 'cover.jpg', - googleAnalyticsId: '', - background_color: '#ffffff', - theme_color: '#222222', - display: 'standalone', - icon: 'content/images/icon.jpeg', - postsPerPage: 10, - headerTitle: 'maxpou.fr', - headerLinks: [ - { - label: 'Blog', - url: '/', - }, - { - label: 'Speaking', - url: '/speaking', - }, - { - label: 'About', - url: '/about', - }, - ], - // Footer information (ex: Github, Netlify...) - websiteHost: { - name: 'GitHub', - url: 'https://github.com/maxpou/maxpou.fr', - }, - footerLinks: [ - { - sectionName: 'Explore', - links: [ - { - label: 'Blog', - url: '/', - }, - { - label: 'Speaking', - url: '/speaking', - }, - { - label: 'Uses', - url: '/uses', - }, - { - label: 'About', - url: '/about', - }, - { - label: 'Resume', - url: '/cv', - }, - { - label: 'Readme', - url: '/readme', - }, - ], - }, - { - sectionName: 'Follow me', - links: [ - { - label: 'GitHub', - url: 'https://github.com/maxpou', - rel: 'me', - }, - { - label: 'Twitter', - url: 'https://twitter.com/_maxpou', - rel: 'me', - }, - { - label: 'LinkedIn', - url: 'https://www.linkedin.com/in/maxpou/', - }, - { - label: 'Nomad List', - url: 'https://nomadlist.com/@maxpou', - }, - { - label: 'Kitchen', - url: './kitchen', - }, - { - label: 'RSS', - url: '/rss', - }, - ], - }, - ], -} diff --git a/gatsby-browser.js b/gatsby-browser.js deleted file mode 100644 index 57101bd95..000000000 --- a/gatsby-browser.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react' - -import App from './src/components/App' - -export const wrapRootElement = ({ element }) => { - return {element} -} diff --git a/gatsby-config.js b/gatsby-config.js deleted file mode 100644 index 489ef374e..000000000 --- a/gatsby-config.js +++ /dev/null @@ -1,179 +0,0 @@ -const path = require('path') -const config = require('./data/siteConfig') -const resume = require('./data/resume-maxpou') - -module.exports = { - siteMetadata: { - title: config.siteTitle, - author: config.authorName, - description: config.siteDescription, - ...config, - resume, - }, - pathPrefix: config.pathPrefix, - trailingSlash: 'ignore', - plugins: [ - { - resolve: `gatsby-plugin-plausible`, - options: { - domain: `maxpou.fr`, - }, - }, - { - resolve: `gatsby-source-filesystem`, - options: { - name: 'posts', - path: 'content/posts', - }, - }, - { - resolve: `gatsby-source-filesystem`, - options: { - name: 'pages', - path: 'content/pages', - }, - }, - { - resolve: `gatsby-source-filesystem`, - options: { - name: 'recipes', - path: 'content/recipes', - }, - }, - { - resolve: `gatsby-source-filesystem`, - options: { - name: 'images', - path: 'content/images', - }, - }, - { - resolve: `gatsby-plugin-page-creator`, - options: { - path: path.join(__dirname, `src`, `pages`), - }, - }, - { - resolve: `gatsby-plugin-mdx`, - options: { - extensions: [`.mdx`, `.md`], - gatsbyRemarkPlugins: [ - { - resolve: 'gatsby-remark-images', - options: { - maxWidth: 590, - linkImagesToOriginal: false, - withWebp: true, - }, - }, - { resolve: 'gatsby-remark-prismjs' }, - { resolve: 'gatsby-remark-responsive-iframe' }, - { resolve: 'gatsby-remark-copy-linked-files' }, - { resolve: 'gatsby-remark-smartypants' }, - { resolve: 'gatsby-remark-autolink-headers' }, - ], - }, - }, - // Reminder (https://github.com/gatsbyjs/gatsby/issues/15486#issuecomment-509405867) - { - resolve: `gatsby-transformer-remark`, - options: { - plugins: [`gatsby-remark-images`], - }, - }, - `gatsby-transformer-sharp`, - `gatsby-plugin-image`, - `gatsby-plugin-styled-components`, - `gatsby-plugin-react-helmet`, - `gatsby-plugin-sharp`, - { - resolve: `gatsby-plugin-manifest`, - options: { - name: config.siteTitle, - short_name: config.siteTitle, - start_url: config.pathPrefix, - background_color: config.background_color, - theme_color: config.theme_color, - display: config.display, - icon: 'content/images/avatar.jpeg', - }, - }, - { - resolve: `gatsby-plugin-sitemap`, - options: {}, - }, - { - resolve: `gatsby-plugin-feed`, - options: { - query: ` - { - site { - siteMetadata { - title - description - siteUrl - site_url: siteUrl - } - } - } - `, - feeds: [ - getBlogFeed('eq: "en"', { - output: '/rss-en.xml', - title: 'Maxence Poutord β€’ Maxpou Blog (EN)', - }), - getBlogFeed('ne: "en"', { - output: '/rss-fr.xml', - title: 'Maxence Poutord β€’ Maxpou Blog (FR)', - }), - getBlogFeed(null, { - output: '/rss.xml', - title: 'Maxence Poutord β€’ Maxpou Blog', - }), - ], - }, - }, - ], -} - -function getBlogFeed(filter, overrides) { - const queryFilter = filter ? `language: {${filter}}` : '' - - return { - serialize: ({ query: { site, allMarkdownRemark } }) => { - return allMarkdownRemark.edges.map(edge => { - return Object.assign({}, edge.node.frontmatter, { - description: edge.node.excerpt, - date: edge.node.frontmatter.date, - url: site.siteMetadata.siteUrl + '/' + edge.node.frontmatter.slug, - guid: site.siteMetadata.siteUrl + '/' + edge.node.frontmatter.slug, - }) - }) - }, - query: ` - { - allMarkdownRemark: allMdx( - sort: { frontmatter: { date: DESC } } - filter: { - frontmatter: { - published: { ne: false } - ${queryFilter} - } - } - ) { - edges { - node { - excerpt(pruneLength: 500) - frontmatter { - title - date - slug - } - } - } - } - } - `, - ...overrides, - } -} diff --git a/gatsby-node.js b/gatsby-node.js deleted file mode 100644 index c1e0a7dce..000000000 --- a/gatsby-node.js +++ /dev/null @@ -1,182 +0,0 @@ -const { createFilePath } = require('gatsby-source-filesystem') -const readingTime = require('reading-time') - -exports.createPages = async ({ graphql, actions, reporter }) => { - const { createPage } = actions - - const BlogPostTemplate = require.resolve('./src/templates/blog-post.js') - const PageTemplate = require.resolve('./src/templates/page.js') - const RecipeTemplate = require.resolve('./src/templates/recipe.js') - const PostsBytagTemplate = require.resolve('./src/templates/tags.js') - const ListPostsTemplate = require.resolve( - './src/templates/blog-list-template.js' - ) - - const allMarkdownQuery = await graphql(` - { - allMarkdown: allMdx( - sort: { frontmatter: { date: DESC } } - filter: { frontmatter: { published: { ne: false } } } - limit: 1000 - ) { - edges { - node { - internal { - contentFilePath - } - fields { - timeToRead { - text - } - } - frontmatter { - title - slug - tags - language - featured - cover { - publicURL - childImageSharp { - gatsbyImageData(width: 640) - } - } - unlisted - } - excerpt - } - } - } - } - `) - - if (allMarkdownQuery.errors) { - reporter.panic(allMarkdownQuery.errors) - } - - const postPerPageQuery = await graphql(` - { - site { - siteMetadata { - postsPerPage - } - } - } - `) - - const markdownFiles = allMarkdownQuery.data.allMarkdown.edges - - const posts = markdownFiles.filter(item => - item.node.internal.contentFilePath.includes('/content/posts/') - ) - - const listedPosts = posts.filter( - item => item.node.frontmatter.unlisted !== true - ) - - const featuredPosts = listedPosts.filter( - item => item.node.frontmatter.featured === true - ) - - // generate paginated post list - const postsPerPage = postPerPageQuery.data.site.siteMetadata.postsPerPage - const nbPages = Math.ceil(listedPosts.length / postsPerPage) - - Array.from({ length: nbPages }).forEach((_, i) => { - createPage({ - path: i === 0 ? `/` : `/pages/${i + 1}`, - component: ListPostsTemplate, - context: { - limit: postsPerPage, - skip: i * postsPerPage, - currentPage: i + 1, - nbPages: nbPages, - }, - }) - }) - - // generate blog posts - posts.forEach((post, index, posts) => { - const shuffleFeaturedPosts = featuredPosts - .filter(p => p.node.frontmatter.slug !== post.node.frontmatter.slug) - .sort(() => 0.5 - Math.random()) - const [previous, next] = shuffleFeaturedPosts.slice(0, 2) - createPage({ - path: post.node.frontmatter.slug, - component: `${BlogPostTemplate}?__contentFilePath=${post.node.internal.contentFilePath}`, - context: { - slug: post.node.frontmatter.slug, - previous: previous.node, - next: next.node, - }, - }) - }) - - // generate pages - markdownFiles - .filter(item => - item.node.internal.contentFilePath.includes('/content/pages/') - ) - .forEach(page => { - createPage({ - path: page.node.frontmatter.slug, - component: `${PageTemplate}?__contentFilePath=${page.node.internal.contentFilePath}`, - context: { - slug: page.node.frontmatter.slug, - }, - }) - }) - - // generate recipes - markdownFiles - .filter(item => - item.node.internal.contentFilePath.includes('/content/recipes/') - ) - .forEach(recipe => { - createPage({ - path: 'kitchen/' + recipe.node.frontmatter.slug, - component: `${RecipeTemplate}?__contentFilePath=${recipe.node.internal.contentFilePath}`, - context: { - slug: recipe.node.frontmatter.slug, - }, - }) - }) - - // generate tag page - markdownFiles - .filter(item => item.node.frontmatter.tags !== null) - .reduce( - (acc, cur) => [...new Set([...acc, ...cur.node.frontmatter.tags])], - [] - ) - .forEach(uniqTag => { - createPage({ - path: `tags/${uniqTag}`, - component: PostsBytagTemplate, - context: { - tag: uniqTag, - }, - }) - }) -} - -exports.onCreateNode = ({ node, actions, getNode }) => { - const { createNodeField } = actions - - if (node.internal.type === `Mdx`) { - createNodeField({ - node, - name: `timeToRead`, - value: readingTime(node.body), - }) - } - - if (node.internal.type === `MarkdownRemark`) { - const value = createFilePath({ node, getNode }) - createNodeField({ - name: `slug`, - node, - value, - }) - } -} diff --git a/gatsby-ssr.js b/gatsby-ssr.js deleted file mode 100644 index 4bd94a085..000000000 --- a/gatsby-ssr.js +++ /dev/null @@ -1,91 +0,0 @@ -import React from 'react' -import Terser from 'terser' - -import { - COLOR_MODE_KEY, - COLORS, - INITIAL_COLOR_MODE_CSS_PROP, -} from './src/tokens/colors' - -import App from './src/components/App' - -function setColorsByTheme() { - const colors = '🌈' - const colorModeKey = 'πŸ”‘' - const colorModeCssProp = '⚑️' - - const mql = window.matchMedia('(prefers-color-scheme: dark)') - const prefersDarkFromMQ = mql.matches - const persistedPreference = localStorage.getItem(colorModeKey) - - let colorMode = 'light' - - const hasUsedToggle = typeof persistedPreference === 'string' - - if (hasUsedToggle) { - colorMode = persistedPreference - } else { - colorMode = prefersDarkFromMQ ? 'dark' : 'light' - } - - let root = document.documentElement - - root.style.setProperty(colorModeCssProp, colorMode) - Object.entries(colors).forEach(([name, colorByTheme]) => { - const cssVarName = `--color-${name}` - - typeof colorByTheme === 'object' - ? root.style.setProperty(cssVarName, colorByTheme[colorMode]) - : root.style.setProperty(cssVarName, colorByTheme) - }) -} - -const MagicScriptTag = () => { - const boundFn = String(setColorsByTheme) - .replace("'🌈'", JSON.stringify(COLORS)) - .replace('πŸ”‘', COLOR_MODE_KEY) - .replace('⚑️', INITIAL_COLOR_MODE_CSS_PROP) - - let calledFunction = `(${boundFn})()` - - calledFunction = Terser.minify(calledFunction).code - - // eslint-disable-next-line react/no-danger - return + + + + + + + + + +{image && } + + + + + + +{image && } + +{ + translations.length > 1 && + translations.map(({ url, lang }) => ( + + )) +} + + diff --git a/src/components/Bio.js b/src/components/Bio.js deleted file mode 100644 index 3cb41694c..000000000 --- a/src/components/Bio.js +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react' -import styled from 'styled-components' -import { Text } from './Commons' -import FollowMe from './FollowMe' -import useSiteMetadata from '../hooks/use-site-config' -import { StaticImage } from 'gatsby-plugin-image' - -const BioWrapper = styled.div` - & .author-image { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - display: block; - position: absolute; - top: -40px; - left: 50%; - margin-left: -40px; - width: 80px; - height: 80px; - border-radius: 100%; - overflow: hidden; - padding: 6px; - z-index: 2; - box-shadow: #ececec 0 0 0 1px; - background-color: var(--color-wrapperBackground); - } - - & .author-image img { - border-radius: 100%; - } - - & .author-profile .author-image { - position: relative; - left: auto; - top: auto; - width: 120px; - height: 120px; - padding: 3px; - margin: -100px auto 0 auto; - box-shadow: none; - } - - & .followMeWrapper { - padding-top: 10px; - } -` - -const BioText = styled(Text)` - & a { - box-shadow: 0 2px 0 0 var(--color-secondary); - } - & a:hover { - filter: brightness(150%); - box-shadow: none; - } -` - -const Bio = () => { - const { authorName, authorDescription } = useSiteMetadata() - - return ( - -
- -
-
-

About the author

- -
- -
-
-
- ) -} - -export default Bio diff --git a/src/components/BlogCard.astro b/src/components/BlogCard.astro new file mode 100644 index 000000000..999f3a8ae --- /dev/null +++ b/src/components/BlogCard.astro @@ -0,0 +1,55 @@ +--- +import type { CollectionEntry } from 'astro:content' +import { Image } from 'astro:assets' +import FrFlag from './icons/fr-flag.png' +import FormattedDate from './FormattedDate.astro' + +type Props = { + post: CollectionEntry<'blog'> + class?: string + firstBigger?: boolean +} + +const { post, firstBigger = true } = Astro.props +--- + + + { + post.data.cover && ( + {`Cover + ) + } +
+
+ + { + post.data.language === 'fr' && ( + French post + ) + } + {post.data.title} + +
+

+ +

+
+
diff --git a/src/components/Commons.js b/src/components/Commons.js deleted file mode 100644 index 72df25370..000000000 --- a/src/components/Commons.js +++ /dev/null @@ -1,102 +0,0 @@ -import React from 'react' -import styled from 'styled-components' -import { createGlobalStyle } from 'styled-components' -import { Link as GatsbyLink } from 'gatsby' - -export const GlobalStyle = createGlobalStyle` -* { - box-sizing: border-box; - margin: 0; - padding: 0; - transition: background 1s ease; -} - -body { - font-family: "Lato", sans-serif; - color: var(--color-text); - background-color: var(--color-siteBackground); -} - -::-webkit-scrollbar { - width: 0.5rem; -} - -::-webkit-scrollbar-thumb { - background: linear-gradient(var(--color-lightYellow), var(--color-secondary)); - border-radius: 8px; - - &:hover { - background: linear-gradient(var(--color-secondary), var(--color-lightYellow)); - } -} - - -img { - max-width: 100%; - height: auto; - vertical-align: middle; - border: 0; -} - -a { - text-decoration: none; - color: var(--color-text); -} - -hr { - border: 0; - border-top: 1px solid var(--color-grey100); - margin: 50px 0 5px 0; -} - -ul, -ol { - padding-left: 2em; - margin: 1em 0 0 0; -} - -*::selection { - background-color: var(--color-secondary); - color: var(--color-grey500); -} -` -export const Link = styled.a` - box-shadow: 0 2px 0 0 var(--color-secondary); - - &:hover { - filter: brightness(150%); - box-shadow: none; - } -` - -export const StyledLink = styled(GatsbyLink)` - box-shadow: 0 2px 0 0 var(--color-secondary); - - &:hover { - filter: brightness(150%); - box-shadow: none; - } -` - -export const Text = styled.p` - line-height: 1.6; - margin: 1em 0 0 0; -` - -export const Bull = styled.span` - display: inline-block; - color: var(--color-textSecondary); - margin: 0 4px; - &::before { - content: 'β€’'; - } -` - -const ReadingTimeContainer = styled.span` - text-transform: uppercase; - color: var(--color-textSecondary); -` - -export const ReadingTime = props => { - return {props.min} -} diff --git a/src/components/Content.js b/src/components/Content.js deleted file mode 100644 index c61c31dd4..000000000 --- a/src/components/Content.js +++ /dev/null @@ -1,175 +0,0 @@ -import React from 'react' -import styled from 'styled-components' -import ContentHeader from './ContentHeader' - -const ContentBody = styled.div` - line-height: 1.6; - font-size: 1.1em; - - & > h2 { - color: var(--color-h2); - padding-top: 3rem; - margin-top: 3rem; - border-top: 1px solid #ececec; - } - - & > h3 { - padding-top: 3rem; - } - - & > p { - margin: 1em 0 0 0; - } - - & li { - margin-bottom: 16px; - - &:last-of-type { - margin-bottom: 0px; - } - ul { - margin-top: 5px; - li { - margin-bottom: 8px; - } - } - } - - & a { - fill: var(--color-text); - position: relative !important; - box-shadow: 0 2px 0 0 var(--color-secondary); - - &:hover { - filter: brightness(150%); - box-shadow: none; - } - - &.anchor, - &.gatsby-resp-image-link { - box-shadow: none; - } - } - - h1 .anchor svg, - h2 .anchor svg, - h3 .anchor svg, - h4 .anchor svg, - h5 .anchor svg, - h6 .anchor svg { - visibility: hidden; - margin-left: -16px; - } - - h1:hover .anchor svg, - h2:hover .anchor svg, - h3:hover .anchor svg, - h4:hover .anchor svg, - h5:hover .anchor svg, - h6:hover .anchor svg, - h1 .anchor:focus svg, - h2 .anchor:focus svg, - h3 .anchor:focus svg, - h4 .anchor:focus svg, - h5 .anchor:focus svg, - h6 .anchor:focus svg { - visibility: visible; - } - - & > blockquote { - box-sizing: border-box; - background-color: var(--color-secondaryContentBackground); - border-left: 5px solid var(--color-secondary); - margin: 30px 0px; - padding: 5px 20px; - border-radius: 0 8px 8px 0; - } - - & > blockquote p { - margin: 0.8em 0; - font-style: italic; - } - - & .gatsby-highlight { - border-radius: 5px; - font-size: 15px; - line-height: 1.7; - border-radius: 10px; - overflow: auto; - tab-size: 1.5em; - margin: 1.5em -1.5em; - - @media (max-width: 500px) { - border-radius: 0; - margin-left: -25px; - margin-right: -25px; - } - } - - & .gatsby-highlight > pre { - border: 0; - margin: 0; - padding: 1; - } - - & .gatsby-highlight pre[class*='language-'] { - float: left; - min-width: 100%; - } - - & .gatsby-highlight-code-line { - background-color: var(--color-darkBlue); - display: block; - margin-right: -1em; - margin-left: -1em; - padding-right: 1em; - padding-left: 0.75em; - border-left: 0.25em solid var(--color-yellow); - } - - & code.language-text { - background: var(--color-beige); - color: #222222cc; - padding: 0 3px; - font-size: 0.94em; - border-radius: 0.3rem; - word-wrap: break-word; - } - - & code { - word-wrap: break-word; - } - - & table { - margin-top: 1em; - margin-bottom: 1em; - border-collapse: collapse; - overflow: hidden; - - & th, - & td { - padding: 0.5em; - background-color: var(--color-secondaryContentBackground); - } - & tr { - border-bottom: 2px solid var(--color-white); - } - & tbody tr:last-child { - border-bottom: none; - } - } -` - -function Content({ children, date, tags, translations }) { - return ( -
- {(tags || date || translations) && ( - - )} - - {children} -
- ) -} - -export default Content diff --git a/src/components/ContentHeader.js b/src/components/ContentHeader.js deleted file mode 100644 index f5486f088..000000000 --- a/src/components/ContentHeader.js +++ /dev/null @@ -1,30 +0,0 @@ -import React, { Fragment } from 'react' -import styled from 'styled-components' -import TagList from './TagList' -import Translations from './Translations' -import Time from './Time' -import { Bull } from './Commons' - -const Header = styled.header` - margin-bottom: 2rem; - color: var(--color-textSecondary); - font-size: 0.9em; -` - -function ContentHeader({ date, tags, translations }) { - return ( -
- {date &&
- ) -} - -export default ContentHeader diff --git a/src/components/DarkModeButton.tsx b/src/components/DarkModeButton.tsx new file mode 100644 index 000000000..f4cee8ca7 --- /dev/null +++ b/src/components/DarkModeButton.tsx @@ -0,0 +1,55 @@ +import React from 'preact/compat' +import type { JSX } from 'preact' +import { useEffect, useState } from 'preact/hooks' +import MoonIcon from './icons/moon.svg' +import SunIcon from './icons/sun.svg' + +type ThemeToggleProps = { + className?: string + textOnly?: boolean +} + +export default function ThemeToggle({ + className, + textOnly = false, +}: ThemeToggleProps): JSX.Element { + const [theme, setTheme] = useState( + window.localStorage.getItem('theme') ?? 'light', + ) + + const handleClick = () => { + setTheme(theme === 'light' ? 'dark' : 'light') + } + + useEffect(() => { + if (theme === 'dark') { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } + window.localStorage.setItem('theme', theme) + }, [theme]) + + return ( + + ) +} diff --git a/src/components/DarkToggle.js b/src/components/DarkToggle.js deleted file mode 100644 index b8982804a..000000000 --- a/src/components/DarkToggle.js +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react' -import styled, { keyframes } from 'styled-components' -import { ThemeContext } from '../ThemeContext' -import MoonIcon from './icons/moon.svg' -import SunIcon from './icons/sun.svg' - -const rotate = keyframes` - from { - transform: rotate(0deg); - } - - to { - transform: rotate(180deg); - } -` - -const moonEffect = keyframes` - from { - transform: rotate(-30deg) translateY(-100%); - opacity: 0.2; - } - to { - transform: rotate(0deg) translateY(0%); - opacity: 1; - } -` - -const DarkModeButton = styled.button` - background: transparent; - cursor: pointer; - border: none; - padding: 8px 10px; - color: #ffffff; - font-weight: 700; - margin: 10px 0; - - &:hover { - filter: invert(0.7) sepia(1.3) saturate(1.6) brightness(2.4); - } - & .moon { - animation: ${moonEffect} 1s linear; - } - & .sun { - animation: ${rotate} 1s linear; - } -` - -const DarkToggle = ({ isExpanded = false }) => { - const { colorMode, setColorMode } = React.useContext(ThemeContext) - - if (!colorMode) { - return null - } - - const toggleMode = () => - colorMode === 'light' ? setColorMode('dark') : setColorMode('light') - const oppositeColor = colorMode === 'light' ? 'dark' : 'light' - - return ( - - {colorMode === 'dark' ? ( - sun logo - ) : ( - moon Logo - )} - {isExpanded && `Toggle ${oppositeColor} mode`} - - ) -} - -export default DarkToggle diff --git a/src/components/Flag.js b/src/components/Flag.js deleted file mode 100644 index 94ed7709f..000000000 --- a/src/components/Flag.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' -import styled from 'styled-components' -import ukFlag from './icons/uk-flag.png' -import frFlag from './icons/fr-flag.png' - -const FlagImage = styled.img` - padding-right: 0.5rem; - padding-bottom: 0.2rem; -` - -function Flag({ language }) { - const img = language === 'en' ? ukFlag : frFlag - const alt = language === 'en' ? 'english post' : 'post en francais' - - return -} - -export default Flag diff --git a/src/components/FollowMe.js b/src/components/FollowMe.js deleted file mode 100644 index e84f2e6c1..000000000 --- a/src/components/FollowMe.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react' -import styled from 'styled-components' -import useSiteMetadata from '../hooks/use-site-config' -import TwitterIcon from '../components/icons/twitter' - -const TwitterButton = styled.a` - background-color: #1da1f2; - border-color: #1da1f2; - color: #fff; - font-size: 0.9rem; - border-radius: 3px; - padding: 10px 20px 10px 25px; - font-weight: 600; - transition: background 0s; - - &:hover { - background-color: #fff; - color: #1da1f2; - border: 1px solid #1da1f2; - } - - & > svg { - transform: translateX(-50%) translateY(15%); - } -` - -const FollowMe = ({ ...props }) => { - const { twitterUsername } = useSiteMetadata() - return ( - - - Follow @{twitterUsername} - - ) -} - -export default FollowMe diff --git a/src/components/Footer.astro b/src/components/Footer.astro new file mode 100644 index 000000000..ec1beb72e --- /dev/null +++ b/src/components/Footer.astro @@ -0,0 +1,82 @@ +--- +import { SITE_OWNER } from '../consts' + +const today = new Date() +--- + + diff --git a/src/components/Footer.js b/src/components/Footer.js deleted file mode 100644 index 897e631b7..000000000 --- a/src/components/Footer.js +++ /dev/null @@ -1,168 +0,0 @@ -import React from 'react' -import { Link } from 'gatsby' -import styled from 'styled-components' -import useSiteMetadata from '../hooks/use-site-config' - -const FooterWrapper = styled.footer` - text-align: left; - padding-top: 30px; - padding-bottom: 50px; - background-color: var(--color-primary); - color: var(--color-white); - padding-left: 20px; - padding-right: 20px; - margin: 0 auto; - - & nav { - display: flex; - flex-flow: row wrap; - align-items: flex-start; - max-width: 900px; - margin: 0 auto; - - .footer-col { - flex: 1 auto; - display: inline-flex; - flex-direction: column; - padding-right: 1em; - } - } - - & a { - color: var(--color-white); - font-weight: bold; - - &:hover { - color: var(--color-grey200); - } - } - - .footer-col > p { - margin: 0; - } - - .footer-title { - font-size: 0.83em; - margin: 0 0 1rem; - } - - .footer-item { - color: var(--color-white); - - & a { - padding: 0.25rem 0; - display: block; - } - } - - .footer-heart { - color: var(--color-red); - } - - .footer-item-text { - padding: 0.1rem 0; - color: var(--color-white); - } - - .footer-header { - order: 1; - margin: 0 0.25rem; - margin-right: 0.25rem; - padding: 0.25rem; - } - - .footer-column-items { - grid-template-columns: 1fr; - display: grid; - } - - @media (max-width: 564px) { - .footer-col:first-child { - width: 100%; - padding-bottom: 2em; - } - } -` - -const Footer = () => { - const { authorName, websiteHost, footerLinks } = useSiteMetadata() - - const FooterItem = ({ item }) => { - if (item.url.startsWith('/')) { - return ( - - - {item.label} - - - ) - } - return ( - - - {item.label} - - - ) - } - - const FooterColumn = ({ column }) => { - return ( -
-

- {column.sectionName} -

-
- {column.links.map((item, i) => { - return - })} -
-
- ) - } - - return ( - - - - ) -} - -export default Footer diff --git a/src/components/FormattedDate.astro b/src/components/FormattedDate.astro new file mode 100644 index 000000000..bcea07538 --- /dev/null +++ b/src/components/FormattedDate.astro @@ -0,0 +1,17 @@ +--- +interface Props { + date: Date +} + +const { date } = Astro.props +--- + + diff --git a/src/components/Header.astro b/src/components/Header.astro new file mode 100644 index 000000000..796773e28 --- /dev/null +++ b/src/components/Header.astro @@ -0,0 +1,95 @@ +--- +import HeaderLink from './HeaderLink.astro' +import DarkModeButton from './DarkModeButton.tsx' +import { SITE_TITLE } from '../consts' +--- + +
+ + +
+ + diff --git a/src/components/Header.js b/src/components/Header.js deleted file mode 100644 index 620916b7c..000000000 --- a/src/components/Header.js +++ /dev/null @@ -1,240 +0,0 @@ -import React, { useState } from 'react' -import { Link } from 'gatsby' -import styled from 'styled-components' - -import useSiteMetadata from '../hooks/use-site-config' -import { media } from '../tokens' -import DarkToggle from './DarkToggle' - -const HeaderWrapper = styled.header` - top: 0; - left: 0; - margin: 0 auto; - display: block; - width: 100%; - z-index: 1000; - background-color: var(--color-primaryAlpha); - font-weight: 700; - - @media ${media.medium} { - position: fixed; - } -` - -const HeaderNav = styled.nav` - font-weight: 700; - margin-left: auto; - margin-right: auto; - height: 60px; - display: flex; - flex-direction: row; - max-width: 770px; - z-index: 1000; - justify-content: space-between; - overflow-x: auto; - overflow-y: hidden; - white-space: nowrap; - padding: 0px 10px; -` - -const HeaderLinksContainer = styled.div` - display: none; - -webkit-box-align: center; - align-items: center; - @media ${media.medium} { - display: flex; - } -` - -const HeaderLink = styled(Link)` - position: relative; - display: flex; - align-items: center; - color: var(--color-white); - border: 0; - margin: 0; - padding: 8px 10px; - - min-width: 42px; - z-index: 10; - - &:hover { - color: var(--color-secondary); - } - - & + & { - margin-left: 0.7rem; - } -` - -const MobilePanel = styled.div` - position: absolute; - z-index: 20; - left: 0; - top: 0; - width: 100vw; - height: 100vh; - display: flex; - align-items: center; - background-color: var(--color-primaryAlpha); - backdrop-filter: blur(10px); - @media ${media.medium} { - display: none; - } -` - -const SkipMainContent = styled.a` - position: absolute; - left: -999px; - width: 1px; - height: 1px; - top: auto; - color: var(--color-white); - background-color: var(--color-grey700); - - &:focus { - display: inline-block; - height: auto; - width: auto; - position: static; - padding: 20px 10px; - } -` - -const MobileNav = styled.nav` - max-width: 800px; - display: flex; - flex-direction: column; - -webkit-box-align: center; - align-items: center; - justify-content: space-evenly; - margin: 0px auto; - - & a { - display: flex; - margin: 10px 0 !important; - } -` - -const HeaderLinks = ({ headerLinks }) => { - return headerLinks.map((headerLink, i) => ( - - {headerLink.label} - - )) -} - -const BurgerButton = styled.button` - z-index: 30; - top: 0px; - position: relative; - color: var(--color-white); - display: flex; - background: transparent; - border: none; - cursor: pointer; - padding: 8px 12px; - -webkit-tap-highlight-color: transparent; - - @media ${media.medium} { - display: none; - } -` - -const BurgerContent = styled.div` - width: 24px; - top: 30px; - height: 2px; - background: var(--color-white); - position: absolute; - left: 0; - ${props => - props.$istoggledon - ? 'background: transparent;' - : `background: var(--color-white);`} - transition: all 250ms cubic-bezier(0.86, 0, 0.07, 1); - - &:before { - content: ''; - top: -8px; - width: 24px; - height: 2px; - background: var(--color-white); - position: absolute; - left: 0; - ${props => - props.$istoggledon - ? 'transform: rotate(45deg); top: 0;' - : 'transform: rotate(0);'}; - transition: all 250ms cubic-bezier(0.86, 0, 0.07, 1); - } - &:after { - top: 8px; - content: ''; - width: 24px; - height: 2px; - background: white; - position: absolute; - left: 0; - ${props => - props.$istoggledon - ? 'transform: rotate(-45deg); top: 0;' - : 'transform: rotate(0);'}; - transition: all 250ms cubic-bezier(0.86, 0, 0.07, 1); - } -` - -const MobileHeader = ({ headerLinks }) => { - const [isToggledOn, setToggle] = useState(false) - const toggle = () => setToggle(!isToggledOn) - - return ( - <> - - - - {isToggledOn && ( - - - - - - - )} - - ) -} - -const Header = () => { - const { headerLinks, headerTitle } = useSiteMetadata() - - return ( - - - - Skip to main content - - - - {headerTitle} - - - - - - - - - - - ) -} - -export default Header diff --git a/src/components/HeaderLink.astro b/src/components/HeaderLink.astro new file mode 100644 index 000000000..f76767a59 --- /dev/null +++ b/src/components/HeaderLink.astro @@ -0,0 +1,34 @@ +--- +import type { HTMLAttributes } from 'astro/types' + +type Props = HTMLAttributes<'a'> + +const { href, class: className, ...props } = Astro.props + +const { pathname } = Astro.url +const isActive = href === pathname || href === pathname.replace(/\/$/, '') +--- + + + + + diff --git a/src/components/Hero.js b/src/components/Hero.js deleted file mode 100644 index ee02fa7d0..000000000 --- a/src/components/Hero.js +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react' -import styled from 'styled-components' -import { GatsbyImage, getImage, StaticImage } from 'gatsby-plugin-image' - -const HeroContainer = styled.div` - position: relative; - display: grid; - width: 100%; - height: 400px; -` - -const TitleContainer = styled.div` - grid-area: 1/1; - position: relative; - place-items: center; - display: grid; -` - -const HeroTitle = styled.h1` - text-align: center; - font-weight: 700; - font-size: 3rem; - margin: 10px 50px; - color: var(--color-white); - text-shadow: 1px 1px 4px rgba(34, 34, 34, 0.85); -` - -const HeroSubTitle = styled.h2` - margin: 10px 50px; - color: var(--color-white); - text-shadow: 1px 1px 4px rgba(34, 34, 34, 0.85); - text-align: center; -` - -const Hero = props => { - const siteCoverPath = `../../content/images/cover.jpg` - const heroImg = getImage(props.heroImg) || siteCoverPath - const isDynamic = !!getImage(props.heroImg) - - return ( - - {isDynamic && ( - - )} - {!isDynamic && ( - - )} - -
- {props.title} - {props.subTitle && {props.subTitle}} -
-
-
- ) -} - -export default Hero diff --git a/src/components/HeroImage.astro b/src/components/HeroImage.astro new file mode 100644 index 000000000..63ef95225 --- /dev/null +++ b/src/components/HeroImage.astro @@ -0,0 +1,23 @@ +--- +import { Image } from 'astro:assets' + +// type Props = AstroAssets.Image + +const { src, alt } = Astro.props +--- + +
+ + {alt} +
diff --git a/src/components/Kitchen/Layout.js b/src/components/Kitchen/Layout.js deleted file mode 100644 index 277fab0fb..000000000 --- a/src/components/Kitchen/Layout.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react' -import { Helmet } from 'react-helmet' -import { GlobalStyle } from '../Commons' -import styled from 'styled-components' -import { Link } from 'gatsby' - -const Footer = styled.footer` - padding: 20px; - background-color: var(--color-primary); - color: var(--color-white); - text-align: center; -` - -const FooterLink = styled(Link)` - color: var(--color-white); - &:hover { - color: var(--color-white); - text-decoration: underline; - } -` - -export const Layout = ({ children }) => { - return ( - <> - - - - - - - {children} -
- Maxence Poutord Β©{' '} - {new Date().getFullYear()} -
- - ) -} diff --git a/src/components/Layout.astro b/src/components/Layout.astro new file mode 100644 index 000000000..1104e8047 --- /dev/null +++ b/src/components/Layout.astro @@ -0,0 +1,46 @@ +--- +import BaseHead from '../components/BaseHead.astro' +import Header from '../components/Header.astro' +import Footer from '../components/Footer.astro' + +interface Props { + title: string + description?: string + lang?: string + translations?: { url: string; lang: string }[] +} + +const { title, description, lang = 'en', translations = [] } = Astro.props +--- + + + + + + + +
+
+ +
+