├── public
├── _redirects
├── favicon.ico
├── logo192.png
├── logo512.png
├── meta-book.png
├── meta-image.png
├── robots.txt
├── social-book.png
├── social-image.png
├── your-website-sucks-sample.pdf
├── sitemap.xml
├── manifest.json
└── index.html
├── src
├── assets
│ ├── book.png
│ ├── twan.JPG
│ ├── thatsanegg.png
│ ├── twan_transparent.png
│ ├── clients
│ │ ├── volkswagen.svg
│ │ ├── accenture.svg
│ │ ├── mastercard.svg
│ │ ├── framer.svg
│ │ └── porsche.svg
│ └── ButtonAccent.js
├── components
│ ├── Intro.js
│ ├── ScrollToTop.js
│ ├── Book.js
│ ├── Timeline
│ │ ├── 1996
│ │ │ └── index.js
│ │ ├── 2016
│ │ │ └── index.js
│ │ ├── 2019
│ │ │ └── index.js
│ │ ├── 2020
│ │ │ └── index.js
│ │ ├── 2021
│ │ │ └── index.js
│ │ ├── 2024
│ │ │ └── index.js
│ │ ├── BlogPost.js
│ │ ├── Entry.js
│ │ └── Timeline.js
│ ├── About.js
│ ├── Book.css
│ ├── Contact.js
│ ├── Clients.js
│ ├── Footer.js
│ ├── Hero.js
│ └── CurrentlyListening.js
├── App.test.js
├── setupTests.js
├── App.js
├── reportWebVitals.js
├── index.js
├── pages
│ ├── Home.js
│ └── WebsiteSucks.js
└── index.css
├── README.md
├── craco.config.js
├── .prettierrc
├── .gitignore
├── tailwind.config.js
└── package.json
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/public/meta-book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/public/meta-book.png
--------------------------------------------------------------------------------
/public/meta-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/public/meta-image.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/assets/book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/src/assets/book.png
--------------------------------------------------------------------------------
/src/assets/twan.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/src/assets/twan.JPG
--------------------------------------------------------------------------------
/public/social-book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/public/social-book.png
--------------------------------------------------------------------------------
/public/social-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/public/social-image.png
--------------------------------------------------------------------------------
/src/assets/thatsanegg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/src/assets/thatsanegg.png
--------------------------------------------------------------------------------
/src/assets/twan_transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/src/assets/twan_transparent.png
--------------------------------------------------------------------------------
/public/your-website-sucks-sample.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twanmulder/portfolio/HEAD/public/your-website-sucks-sample.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # My personal portfolio website 😄
2 |
3 | Check it out over at:
4 |
5 | [www.twanmulder.com](https://www.twanmulder.com)
6 |
--------------------------------------------------------------------------------
/craco.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | style: {
3 | postcss: {
4 | plugins: [require("tailwindcss"), require("autoprefixer")],
5 | },
6 | },
7 | }
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "printWidth": 500,
4 | "semi": false,
5 | "singleQuote": false,
6 | "tabWidth": 2,
7 | "trailingComma": "es5"
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/Intro.js:
--------------------------------------------------------------------------------
1 | export default function Intro() {
2 | return (
3 |
6 | )
7 | }
8 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render } from "@testing-library/react"
2 | import App from "./App"
3 |
4 | test("renders learn react link", () => {
5 | render( )
6 | })
7 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import "@testing-library/jest-dom"
6 |
--------------------------------------------------------------------------------
/src/components/ScrollToTop.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react"
2 | import { useLocation } from "react-router-dom"
3 |
4 | export default function ScrollToTop() {
5 | const { pathname } = useLocation()
6 |
7 | useEffect(() => {
8 | window.scrollTo(0, 0)
9 | }, [pathname])
10 |
11 | return null
12 | }
13 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { Fragment } from "react"
2 |
3 | import Hero from "./components/Hero"
4 | import Main from "./components/Main"
5 | import Footer from "./components/Footer"
6 |
7 | function App() {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | )
15 | }
16 |
17 | export default App
18 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = (onPerfEntry) => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry)
5 | getFID(onPerfEntry)
6 | getFCP(onPerfEntry)
7 | getLCP(onPerfEntry)
8 | getTTFB(onPerfEntry)
9 | })
10 | }
11 | }
12 |
13 | export default reportWebVitals
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .eslintcache
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/src/components/Book.js:
--------------------------------------------------------------------------------
1 | import "./Book.css"
2 | import bookcover from "../assets/book.png"
3 |
4 | export default function Book() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/public/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | https://www.twanmulder.com/ weekly 1
4 |
--------------------------------------------------------------------------------
/src/components/Timeline/2016/index.js:
--------------------------------------------------------------------------------
1 | import { Book } from "react-feather"
2 | import { DateEntry, Notes, TimelineEntry } from "../Entry"
3 |
4 | export function Year2016() {
5 | return (
6 | <>
7 |
8 |
9 |
10 | Started studying Communication & Multi-media Design at the Hanze University of Applied Sciences.
11 |
12 |
13 | >
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Portfolio Twan Mulder",
3 | "name": "Portfolio Twan Mulder",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/Timeline/1996/index.js:
--------------------------------------------------------------------------------
1 | import { Gift } from "react-feather"
2 | import { DateEntry, Notes, TimelineEntry } from "../Entry"
3 |
4 | export function Year1996() {
5 | const dob = new Date("12/07/1996")
6 | const monthDiff = Date.now() - dob.getTime()
7 | const ageDate = new Date(monthDiff)
8 | const year = ageDate.getUTCFullYear()
9 | const ageInYears = Math.abs(year - 1970)
10 |
11 | return (
12 | <>
13 |
14 |
15 |
16 | I was born the 7th of December 1996, which makes me {ageInYears} years old!
17 |
18 |
19 | >
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/src/components/About.js:
--------------------------------------------------------------------------------
1 | import twan from "../assets/twan_transparent.png"
2 |
3 | export default function About() {
4 | return (
5 |
6 |
7 |
8 |
9 | About
10 | Twan Mulder
11 | Front-end Developer who loves writing and creating enticing experiences
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Timeline/BlogPost.js:
--------------------------------------------------------------------------------
1 | import { TimelineEntry } from "./Entry"
2 | import { Edit2 } from "react-feather"
3 |
4 | export function BlogPost({ timelinetitle = "Published new post", slug, title, description, timestamp, divider = true }) {
5 | return (
6 |
7 |
8 |
9 |
10 |
{title}
11 |
{description}
12 |
13 |
14 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import ReactDOM from "react-dom"
3 | import { BrowserRouter, Switch, Route } from "react-router-dom"
4 |
5 | import "./index.css"
6 | import "typeface-poppins"
7 |
8 | import Home from "./pages/Home"
9 | import WebsiteSucks from "./pages/WebsiteSucks"
10 | import ScrollToTop from "./components/ScrollToTop"
11 | import reportWebVitals from "./reportWebVitals"
12 |
13 | ReactDOM.render(
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ,
27 | document.getElementById("root")
28 | )
29 |
30 | // If you want to start measuring performance in your app, pass a function
31 | // to log results (for example: reportWebVitals(console.log))
32 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
33 | reportWebVitals()
34 |
--------------------------------------------------------------------------------
/src/components/Timeline/2024/index.js:
--------------------------------------------------------------------------------
1 | import { Briefcase } from "react-feather"
2 | import { ButtonSet, DateEntry, Notes, TimelineEntry } from "../Entry"
3 |
4 | export function Year2024() {
5 | return (
6 | <>
7 |
8 |
9 |
10 | As Frontend Developer at DHL eCommerce, I'm part of multiple teams that work on a broad range of both internal and external facing tools and websites.
11 | This work spans from creating an entirely new user interface from the ground up for the Customs team, to migrating the API implementation from custom functions to React Query for the Location team, and many more tasks ranging a broad spectrum of technical capabilities.
12 |
13 |
14 |
15 | view website
16 |
17 |
18 |
19 | >
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"],
3 | darkMode: false, // or 'media' or 'class'
4 | theme: {
5 | fontFamily: {
6 | sans: ["Poppins", "Oswald", "ui-sans-serif", "system-ui", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto", "Helvetica Neue", "Arial", "Noto Sans", "sans-serif", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"],
7 | },
8 | extend: {
9 | colors: {
10 | blue: {
11 | 550: "#08f",
12 | },
13 | },
14 | height: {
15 | "9/10-screen": "90vh",
16 | },
17 | zIndex: {
18 | "-1": "-1",
19 | },
20 | margin: {
21 | screen: "100vh",
22 | },
23 | keyframes: {
24 | "fade-in": {
25 | "100%": { opacity: "1" },
26 | },
27 | },
28 | animation: {
29 | "fade-in": "fade-in 1s ease-out 1.8s forwards",
30 | },
31 | typography: {
32 | DEFAULT: {
33 | css: {
34 | blockquote: {
35 | fontWeight: "700",
36 | },
37 | },
38 | },
39 | },
40 | },
41 | },
42 | variants: {
43 | extend: {},
44 | },
45 | plugins: [require("@tailwindcss/typography")],
46 | }
47 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Twan Mulder
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | You need to enable JavaScript to run this app.
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "twan-mulder-portfolio",
3 | "description": "Twan's Portfolio",
4 | "version": "0.1.0",
5 | "private": true,
6 | "dependencies": {
7 | "@craco/craco": "^6.0.0",
8 | "@tailwindcss/postcss7-compat": "^2.0.2",
9 | "@tailwindcss/typography": "^0.3.1",
10 | "@testing-library/jest-dom": "^5.11.6",
11 | "@testing-library/react": "^11.2.2",
12 | "@testing-library/user-event": "^12.6.0",
13 | "autoprefixer": "^9.8.6",
14 | "fast-average-color": "^6.3.0",
15 | "postcss": "^7.0.36",
16 | "react": "^17.0.1",
17 | "react-dom": "^17.0.1",
18 | "react-feather": "^2.0.9",
19 | "react-helmet": "^6.1.0",
20 | "react-router-dom": "^5.2.0",
21 | "react-scripts": "4.0.1",
22 | "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.2",
23 | "typeface-poppins": "^1.1.13",
24 | "use-last-fm": "^0.4.0",
25 | "web-vitals": "^0.2.4"
26 | },
27 | "scripts": {
28 | "start": "craco start",
29 | "build": "craco build",
30 | "test": "craco test",
31 | "eject": "react-scripts eject"
32 | },
33 | "eslintConfig": {
34 | "extends": [
35 | "react-app",
36 | "react-app/jest"
37 | ]
38 | },
39 | "browserslist": {
40 | "production": [
41 | ">0.2%",
42 | "not dead",
43 | "not op_mini all"
44 | ],
45 | "development": [
46 | "last 1 chrome version",
47 | "last 1 firefox version",
48 | "last 1 safari version"
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/Book.css:
--------------------------------------------------------------------------------
1 | .book-container {
2 | perspective: 600px;
3 | }
4 |
5 | .book {
6 | width: 200px;
7 | height: 283px;
8 | position: relative;
9 | transform-style: preserve-3d;
10 | transform: rotateY(-30deg);
11 | transition: transform 1s ease;
12 | animation: 1s ease 0s 1 initAnimation;
13 | }
14 |
15 | .book:hover {
16 | transform: rotateY(0deg);
17 | }
18 |
19 | .book::before {
20 | position: absolute;
21 | content: " ";
22 | left: 0;
23 | top: 3px;
24 | width: 48px;
25 | height: 277px;
26 | transform: translateX(172px) rotateY(90deg);
27 | background: linear-gradient(90deg, #fff 0%, #f9f9f9 5%, #fff 10%, #f9f9f9 15%, #fff 20%, #f9f9f9 25%, #fff 30%, #f9f9f9 35%, #fff 40%, #f9f9f9 45%, #fff 50%, #f9f9f9 55%, #fff 60%, #f9f9f9 65%, #fff 70%, #f9f9f9 75%, #fff 80%, #f9f9f9 85%, #fff 90%, #f9f9f9 95%, #fff 100%);
28 | }
29 |
30 | .book::after {
31 | position: absolute;
32 | top: 0;
33 | left: 0;
34 | content: " ";
35 | width: 200px;
36 | height: 283px;
37 | transform: translateZ(-25px);
38 | background-color: #202833;
39 | border-radius: 0 2px 2px 0;
40 | box-shadow: -10px 0 50px 10px #20283380;
41 | }
42 |
43 | .book img {
44 | margin: 0;
45 | position: absolute;
46 | top: 0;
47 | left: 0;
48 | width: 200px;
49 | height: 283px;
50 | transform: translateZ(25px);
51 | border-radius: 0 2px 2px 0;
52 | box-shadow: 5px 5px 20px #20283380;
53 | background-color: #202833;
54 | }
55 |
56 | @keyframes initAnimation {
57 | 0% {
58 | transform: rotateY(0deg);
59 | }
60 | 100% {
61 | transform: rotateY(-30deg);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/components/Contact.js:
--------------------------------------------------------------------------------
1 | import Twan from "../assets/twan_transparent.png"
2 | import { Linkedin } from "react-feather"
3 |
4 | export default function Contact() {
5 | return (
6 |
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/Timeline/2019/index.js:
--------------------------------------------------------------------------------
1 | import { Briefcase } from "react-feather"
2 | import { BlogPost } from "../BlogPost"
3 | import { ButtonSet, DateEntry, Notes, TimelineEntry } from "../Entry"
4 |
5 | export function Year2019() {
6 | return (
7 | <>
8 |
9 | {/*
10 |
11 | Storm Digital is officially acquired by Accenture Interactive on the 1st of October.
12 |
13 | */}
14 |
15 |
16 |
17 | Started my first job as a Frontend Developer at a digital marketing agency called Storm Digital.
18 | Responsible for creating dynamic, interactive, and animated display creatives using modern technologies such as HTML, CSS, JavaScript, Vue, React, and GSAP. Communicated between stakeholders, designers, and clients to create a fitting end result.
19 |
20 |
21 |
22 | view website
23 |
24 |
25 |
26 | >
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/Clients.js:
--------------------------------------------------------------------------------
1 | import framer from "../assets/clients/framer.svg"
2 | import porsche from "../assets/clients/porsche.svg"
3 | import accenture from "../assets/clients/accenture.svg"
4 | import mastercard from "../assets/clients/mastercard.svg"
5 | import volkswagen from "../assets/clients/volkswagen.svg"
6 | import virginmedia from "../assets/clients/virginmedia.svg"
7 |
8 | export default function Clients() {
9 | return (
10 |
11 |
12 |
13 | Some cool companies
14 |
15 | I've worked with.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom"
2 | import { GitHub, Twitter, Linkedin } from "react-feather"
3 |
4 | export default function Footer() {
5 | return (
6 |
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/src/assets/clients/volkswagen.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/assets/clients/accenture.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
8 |
9 |
10 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/components/Timeline/Entry.js:
--------------------------------------------------------------------------------
1 | function getTint(color) {
2 | switch (color) {
3 | case "red":
4 | return "text-red-800 bg-red-500 bg-opacity-20"
5 | case "blue":
6 | return "text-blue-800 bg-blue-500 bg-opacity-20"
7 | case "green":
8 | return "text-green-800 bg-green-500 bg-opacity-20"
9 | case "purple":
10 | return "text-purple-800 bg-purple-500 bg-opacity-20"
11 | case "indigo":
12 | return "text-indigo-800 bg-indigo-500 bg-opacity-20"
13 | case "pink":
14 | return "text-pink-800 bg-pink-500 bg-opacity-20"
15 | case "yellow":
16 | return "text-yellow-800 bg-yellow-500 bg-opacity-20"
17 | case "gray":
18 | default:
19 | return "text-primary bg-gray-200"
20 | }
21 | }
22 |
23 | export function Notes({ children }) {
24 | return {children}
25 | }
26 |
27 | export function ButtonSet({ children }) {
28 | return {children}
29 | }
30 |
31 | export function TimelineEntry({ children = null, title, timestamp, Icon, tint = "gray", divider = true }) {
32 | return (
33 |
34 | {/* Icon and dividing line */}
35 |
36 |
37 |
38 |
39 | {divider &&
}
40 |
41 |
42 |
43 |
44 |
{title}
45 | {timestamp && {timestamp} }
46 |
47 |
{children}
48 |
49 |
50 | )
51 | }
52 |
53 | export function DateEntry({ title }) {
54 | return (
55 |
56 |
57 |
{title}
58 |
59 |
60 | )
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/Timeline/Timeline.js:
--------------------------------------------------------------------------------
1 | import { Year1996 } from "./1996"
2 | import { Year2016 } from "./2016"
3 | import { Year2019 } from "./2019"
4 | import { Year2020 } from "./2020"
5 | import { Year2021 } from "./2021"
6 | import { Year2024 } from "./2024"
7 |
8 | export default function Timeline() {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | )
46 | }
47 |
--------------------------------------------------------------------------------
/src/components/Timeline/2021/index.js:
--------------------------------------------------------------------------------
1 | import { Zap, Briefcase } from "react-feather"
2 | import { ButtonSet, DateEntry, Notes, TimelineEntry } from "../Entry"
3 |
4 | export function Year2021() {
5 | return (
6 | <>
7 |
8 |
9 |
10 | As part of Central Group & Signa, De Bijenkorf is one of the largest luxury e-commerce platforms in the Netherlands.
11 | As as Frontend Developer in one of the most crucial teams of the company - Checkout, I am responsible for making sure that the customer payment journey is smooth, intuitive, and flawless. Serving more than a million unique visitors each month, a frictionless checkout flow is essential. Some of the technologies we use are: React, TypeScript, Redux, Jest, Playwright, React Testing Library, SCSS.
12 |
13 |
14 |
15 | view website
16 |
17 |
18 |
19 |
20 |
21 | Framer is a digital design tool that allows user to publish their design as a website with a single button push.
22 | As part of the Customer Success team, I worked as a Technical Product Specialist to integrate this tool into enterprise customer's existing workflow. I lead workshops, did technical demo's, helped users out 1-on-1, and created custom code solutions for them to make their integration and workflow as smooth, useful, and joyful as possible.
23 |
24 |
25 |
26 | view website
27 |
28 |
29 |
30 |
31 |
32 | Adding an amazing README to your GitHub project is a great way of introducing new people to the codebase. That's why I've gathered some of the best to get you started in just a few seconds.
33 |
34 |
35 |
36 | view project
37 |
38 |
39 |
40 | >
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import { Helmet } from "react-helmet"
2 |
3 | import Hero from "../components/Hero"
4 | import About from "../components/About"
5 | import Timeline from "../components/Timeline/Timeline.js"
6 | import Clients from "../components/Clients"
7 | import CurrentlyListening from "../components/CurrentlyListening"
8 | import Footer from "../components/Footer"
9 | import { useEffect, useState } from "react"
10 | import Contact from "../components/Contact"
11 |
12 | function Home() {
13 | const [borderRadius, setBorderRadius] = useState(178)
14 | const [margin, setMargin] = useState(8)
15 |
16 | const windowHeight = window.innerHeight
17 |
18 | const listener = () => {
19 | const scrollFromTop = document.body.getBoundingClientRect().top
20 |
21 | if (scrollFromTop > 0) {
22 | const percentage = (1 / (windowHeight * 0.6)) * (scrollFromTop - 0.4 * windowHeight)
23 |
24 | setBorderRadius(percentage * 200)
25 |
26 | if (percentage < 0) {
27 | setMargin(0)
28 | } else {
29 | setMargin(percentage * 8)
30 | }
31 | } else {
32 | setBorderRadius(0)
33 | setMargin(0)
34 | }
35 | }
36 |
37 | useEffect(() => {
38 | window.addEventListener("scroll", listener)
39 | return () => {
40 | window.removeEventListener("scroll", listener)
41 | }
42 | })
43 |
44 | return (
45 | <>
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
67 |
68 |
69 |
70 |
71 |
72 | >
73 | )
74 | }
75 |
76 | export default Home
77 |
--------------------------------------------------------------------------------
/src/components/Hero.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react"
2 | import { GitHub, Twitter } from "react-feather"
3 |
4 | export default function Hero() {
5 | const [textOpacity, setTextOpacity] = useState(1)
6 | const [textTransform, setTextTransform] = useState(0)
7 |
8 | const windowHeight = window.innerHeight
9 |
10 | const listener = () => {
11 | const scrollFromTop = document.body.getBoundingClientRect().top
12 | if (scrollFromTop > 0) {
13 | const percentage = (1 / (windowHeight * 0.6)) * (scrollFromTop - 0.4 * windowHeight)
14 | const transform = (windowHeight / 6 - (windowHeight / 6) * percentage).toFixed(3)
15 |
16 | setTextOpacity(percentage)
17 | setTextTransform(transform)
18 | } else {
19 | if (textOpacity !== 0) {
20 | setTextOpacity(0)
21 | }
22 | if (textTransform !== windowHeight) {
23 | setTextTransform(windowHeight)
24 | }
25 | }
26 | }
27 |
28 | useEffect(() => {
29 | window.addEventListener("scroll", listener)
30 | return () => {
31 | window.removeEventListener("scroll", listener)
32 | }
33 | })
34 |
35 | return (
36 |
37 |
38 |
39 | Hey, my name is Twan!
40 |
41 | Front-end Developer focused on React who loves writing and creating enticing experiences
42 |
43 |
44 |
55 |
56 |
57 | ↓ scroll
58 |
59 |
60 |
61 | )
62 | }
63 |
--------------------------------------------------------------------------------
/src/assets/clients/mastercard.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
37 |
39 |
41 |
42 |
44 | image/svg+xml
45 |
47 |
48 |
49 |
50 |
51 |
54 |
57 |
60 |
68 |
74 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer utilities {
6 | .container {
7 | padding-left: 1.5rem;
8 | padding-right: 1.5rem;
9 | margin-left: auto;
10 | margin-right: auto;
11 | max-width: 1024px;
12 | }
13 |
14 | @media (min-width: 640px) {
15 | .container {
16 | padding-left: 2rem;
17 | padding-right: 2rem;
18 | }
19 | }
20 | }
21 |
22 | .btn {
23 | @apply px-8 py-2 text-sm text-center transition-colors duration-200 bg-white border rounded-md text-blue-550 border-blue-550 hover:text-white hover:bg-blue-550;
24 | }
25 |
26 | .timeline > div:first-child {
27 | margin-top: 75px;
28 | }
29 |
30 | .main {
31 | margin-top: 100vh;
32 | margin-left: calc(0.1 * var(--margin));
33 | margin-right: calc(0.1 * var(--margin));
34 | border-radius: calc(0.25 * var(--border-radius));
35 | }
36 |
37 | @media (min-width: 1200px) {
38 | .main {
39 | margin-top: calc(100vh - 4rem);
40 | margin-left: var(--margin);
41 | margin-right: var(--margin);
42 | border-radius: var(--border-radius);
43 | }
44 | }
45 |
46 | .gradient-mesh {
47 | /* background: #e099ff;
48 | background-image: radial-gradient(at 41% 10%, hsla(254, 79%, 60%, 1) 0, transparent 42%), radial-gradient(at 91% 33%, hsla(286, 69%, 68%, 1) 0, transparent 46%), radial-gradient(at 5% 92%, hsla(240, 63%, 65%, 1) 0, transparent 56%), radial-gradient(at 10% 65%, hsla(265, 72%, 67%, 1) 0, transparent 59%), radial-gradient(at 21% 49%, hsla(165, 61%, 76%, 1) 0, transparent 54%); */
49 | }
50 |
51 | @keyframes slide-up {
52 | 100% {
53 | transform: translate3d(0, 0, 0);
54 | }
55 | }
56 |
57 | @keyframes fade-up {
58 | 100% {
59 | transform: translate3d(0, 0, 0);
60 | opacity: 1;
61 | }
62 | }
63 |
64 | @keyframes fade-up-partial {
65 | 100% {
66 | transform: translate3d(0, 0, 0);
67 | opacity: 0.8;
68 | }
69 | }
70 |
71 | /* .hero-subtitle {
72 | font-size: 1rem;
73 | } */
74 | /*
75 | @media (min-width: 800px) {
76 | .hero-subtitle {
77 | font-size: 1vmin;
78 | }
79 | }
80 |
81 | @media (min-width: 1200px) {
82 | .hero-subtitle {
83 | font-size: 2vmin;
84 | }
85 | } */
86 |
87 | .fade-up-partial {
88 | animation: fade-up-partial 1.25s cubic-bezier(0.075, 0.82, 0.165, 1) 0.5s forwards;
89 | }
90 |
91 | .fade-up {
92 | animation: fade-up 1.5s cubic-bezier(0.075, 0.82, 0.165, 1) 1s forwards;
93 | }
94 |
95 | .fade-up-2 {
96 | animation: fade-up 1.5s cubic-bezier(0.075, 0.82, 0.165, 1) 1.5s forwards;
97 | }
98 |
99 | .slide-up-1 {
100 | animation: slide-up 1.5s cubic-bezier(0.075, 0.82, 0.165, 1) 0.5s forwards;
101 | }
102 |
103 | .slide-up-2 {
104 | animation: slide-up 1.5s cubic-bezier(0.075, 0.82, 0.165, 1) 1s forwards;
105 | }
106 |
107 | @keyframes slide-up-main {
108 | 0% {
109 | opacity: 0;
110 | transform: translateY(20rem);
111 | }
112 | 30% {
113 | opacity: 0;
114 | }
115 | 100% {
116 | opacity: 1;
117 | transform: translateY(0);
118 | }
119 | }
120 |
121 | .slide-up-main {
122 | animation: slide-up-main 1.5s cubic-bezier(0.075, 0.82, 0.165, 1) 1.55s forwards;
123 | }
124 |
--------------------------------------------------------------------------------
/src/components/CurrentlyListening.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react"
2 | import { useLastFM } from "use-last-fm"
3 | import { Music } from "react-feather"
4 | import FastAverageColor from "fast-average-color"
5 |
6 | export default function CurrentlyListening() {
7 | const lastFM = useLastFM("jengelstengel", "b311316612b05f87eb336f4b8b4065ee")
8 |
9 | useEffect(() => {
10 | const fac = new FastAverageColor()
11 | const setBoxShadow = (img) => {
12 | const size = 80
13 | const height = img.naturalHeight
14 | const colorBottom = fac.getColor(img, { top: height - size, height: size })
15 |
16 | img.style.boxShadow = `0px 6px 15px -4px ${colorBottom.hex}80`
17 | }
18 |
19 | const img = document.querySelector(".song-art")
20 |
21 | // Make sure image is finished loading
22 | if (lastFM.status === "playing" && img && img.complete) {
23 | setBoxShadow(img)
24 | } else if (lastFM.status === "playing" && img && !img.complete) {
25 | img.addEventListener("load", function () {
26 | setBoxShadow(img)
27 | })
28 | }
29 | }, [lastFM])
30 |
31 | return (
32 |
33 |
34 |
35 |
36 | Now listening to
37 |
38 | {lastFM.status === "connecting" && (
39 |
48 | )}
49 | {lastFM.status === "playing" && (
50 |
51 |
52 |
53 |
{lastFM.song.name}
54 |
{lastFM.song.artist}
55 |
56 |
57 | )}
58 | {lastFM.status === "idle" && (
59 |
66 | )}
67 |
68 |
69 | )
70 | }
71 |
--------------------------------------------------------------------------------
/src/components/Timeline/2020/index.js:
--------------------------------------------------------------------------------
1 | import { Award } from "react-feather"
2 | import { BlogPost } from "../BlogPost"
3 | import { ButtonSet, DateEntry, Notes, TimelineEntry } from "../Entry"
4 |
5 | export function Year2020() {
6 | return (
7 | <>
8 |
9 |
10 |
11 | After publishing articles for multiple publications, they have been able to gain over 100k unique reads on Medium.
12 |
13 |
14 |
15 | view profile
16 |
17 |
18 |
19 |
20 | {/*
21 |
22 | A personal project where people send me their website/online product and I tell them why it sucks.
23 |
24 |
25 |
26 | view project
27 |
28 |
29 | */}
30 | {/*
31 |
32 | Almost a month after releasing my blog and publishing the first 5 articles, I've been able to gain my first 1000 unique pageviews on "That's an Egg".
33 |
34 | */}
35 | {/*
36 |
37 |
38 |
39 |
That's an Egg
40 |
Released my very own, personal blog called "That's an Egg". Here, I write about web-development focussed on beginner friendly content.
41 |
42 |
view blog
43 |
44 |
45 |
46 |
47 |
48 |
49 | */}
50 | {/*
51 |
52 | Expanded my work-role to Developer & CRO Specialist.
53 |
54 | */}
55 | >
56 | )
57 | }
58 |
--------------------------------------------------------------------------------
/src/assets/clients/framer.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/clients/porsche.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
12 |
14 |
18 |
22 |
26 |
31 |
35 |
40 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/assets/ButtonAccent.js:
--------------------------------------------------------------------------------
1 | export default function ButtonAccent() {
2 | return (
3 |
4 |
8 |
12 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/src/pages/WebsiteSucks.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react"
2 | import { Helmet } from "react-helmet"
3 |
4 | import Book from "../components/Book"
5 | import Footer from "../components/Footer"
6 |
7 | import Twan from "../assets/twan_transparent.png"
8 | import ButtonAccent from "../assets/ButtonAccent"
9 |
10 | import { Star, Twitter } from "react-feather"
11 |
12 | const waitForIFrameResize = () => {
13 | if (!window.iFrameResize) {
14 | return setTimeout(waitForIFrameResize, 50)
15 | }
16 |
17 | window.iFrameResize({ log: false, checkOrigin: false }, "#testimonialto-your-website-sucks-light")
18 | }
19 |
20 | export default function WebsiteSucks() {
21 | useEffect(() => {
22 | waitForIFrameResize()
23 | }, [])
24 |
25 | return (
26 | <>
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | Your
48 |
49 | Website
50 |
51 | Sucks
52 |
53 |
And I'll help you fix it
54 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | "Great & straight-to-the-point!"
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | "100% recommendation!"
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | "Concise with great examples!"
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | Learn the 5 pillars of creating a website that gets you from visitors, to conversions.
106 |
107 |
Hey, your website sucks!
108 |
A pretty bold statement. One I firmly believe in and stand by.
109 |
At the time of writing, there are over 2 billion websites on the internet. 400 million of those are active.
110 |
111 | And I'm sure that{" "}
112 |
113 | all of them suck
114 |
115 | .
116 |
117 |
Working as a Developer and CRO Specialist for companies like Accenture and marketing agencies, I have a lot of experience creating and/or reviewing websites for clients like Audi, Virgin Media, and Porsche.
118 |
Even though there's an amazing amount of great working, good looking websites out there, not one of them is perfect.
119 |
That's why I'd love to help you improve yours!
120 |
In this e-book, we'll go through 5 different pillars of creating a website that gets you the results you want.
121 |
122 | There are still a{" "}
123 |
124 | ton
125 | {" "}
126 | of different things you can work on to improve your website, but these 5 ones are the ones I've found had the most long-term impact.
127 |
128 |
Chapters
129 |
130 |
131 | Target Audience
132 |
133 |
134 | Value Proposition
135 |
136 |
137 | SEO
138 |
139 |
140 | Design & Layout
141 |
142 |
143 | Page Speed
144 |
145 |
146 |
Who is this book for?
147 |
If you're currently not getting the results you want from your own website and want to understand the core reasons of why that is, this book is for you!
148 |
It doesn't matter if you're a Designer, Developer, SaaS Founder, or anything else. As long as you want to make a positive change on the impact your website has, it'll be a great match!
149 |
Refund Policy
150 |
If you're not 100% satisfied with the purchase, or it's not what you were expecting, just reply to the download email within 30 days, and you'll get a full refund. No questions asked.
151 |
152 |
153 | Not convinced just yet?
154 |
155 | Read a sample chapter before committing
156 |
157 |
158 |
159 |
160 | Buy the Book
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 | Turn Visitors,
169 | Into Customers
170 |
171 |
Get a personalized 20min. breakdown of your website.
172 |
Learn where your users get stuck and increase your MMR without spending thousands of dollars.
173 |
174 |
187 |
188 |
189 |
190 |
191 | >
192 | )
193 | }
194 |
--------------------------------------------------------------------------------