Completely reworked website
parent
8d72a759f9
commit
d85095095d
@ -1,9 +0,0 @@
|
|||||||
**
|
|
||||||
|
|
||||||
!next-i18next.config.js
|
|
||||||
!next.config.js
|
|
||||||
!tsconfig.json
|
|
||||||
!package.json
|
|
||||||
!yarn.lock
|
|
||||||
!public
|
|
||||||
!src
|
|
@ -1 +1,5 @@
|
|||||||
CDN_ENDPOINT=cdn.guusvanmeerveld.dev
|
VITE_GITHUB_USERNAME=Guusvanmeerveld
|
||||||
|
VITE_TWITTER_USERNAME=Guusvanmeerveld
|
||||||
|
VITE_KOFI_USERNAME=Guusvanmeerveld
|
||||||
|
VITE_YOUTUBE_CHANNEL_ID=UCYuqpoMay5SezCBrA_HKVWQ
|
||||||
|
VITE_MASTODON_URL=https://c.im/web/@guusvanmeerveld
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"plugins": ["prettier"],
|
|
||||||
"extends": "next/core-web-vitals",
|
|
||||||
"rules": {
|
|
||||||
"prettier/prettier": "error"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +1,42 @@
|
|||||||
name: Build / test application and deploy to Docker hub & Github Pages
|
name: "Deploy website"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
paths:
|
||||||
|
- src/**
|
||||||
|
- package.json
|
||||||
|
- yarn.lock
|
||||||
|
- tsconfig.*
|
||||||
|
- vite.config.ts
|
||||||
|
- .env
|
||||||
|
- index.html
|
||||||
|
- public
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
deploy_to_pages:
|
||||||
|
name: Build pages
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Setup checkout
|
- name: Setup
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup NodeJS v12
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '12.x'
|
node-version: "16.x"
|
||||||
|
cache: "yarn"
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install NPM dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
|
|
||||||
- name: ESlint check
|
- name: Build client
|
||||||
run: yarn lint
|
run: yarn run build
|
||||||
|
|
||||||
pages:
|
- name: Deploy to pages
|
||||||
needs: check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Use Node.js
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: '12.x'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: yarn install
|
|
||||||
|
|
||||||
- name: Export website
|
|
||||||
run: yarn export
|
|
||||||
env:
|
|
||||||
GITHUB_USERNAME: ${{ GITHUB_REPOSITORY_OWNER }}
|
|
||||||
|
|
||||||
- name: Create .nojekyll file
|
|
||||||
run: touch out/.nojekyll
|
|
||||||
|
|
||||||
- name: Deploy
|
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v3
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
publish_dir: out
|
publish_dir: ./dist
|
||||||
|
cname: guusvanmeerveld.dev
|
||||||
docker:
|
|
||||||
needs: check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Setup checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v1.10.0
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
|
|
||||||
- name: Cache Docker layers
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache
|
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-buildx-
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
tags: guusvanmeerveld/portfolio:latest
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
|
||||||
|
|
||||||
- name: Image digest
|
|
||||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
|
||||||
|
@ -1,34 +1,24 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# Logs
|
||||||
|
logs
|
||||||
# dependencies
|
*.log
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# next.js
|
|
||||||
/.next/
|
|
||||||
/out/
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
*.pem
|
|
||||||
|
|
||||||
# debug
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
# local env files
|
lerna-debug.log*
|
||||||
.env.local
|
|
||||||
.env.development.local
|
node_modules
|
||||||
.env.test.local
|
dist
|
||||||
.env.production.local
|
dist-ssr
|
||||||
|
*.local
|
||||||
# vercel
|
|
||||||
.vercel
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
dist
|
|
||||||
node_modules
|
|
||||||
README.md
|
|
||||||
.vscode
|
|
||||||
yarn.lock
|
|
||||||
package-lock.json
|
|
||||||
LICENSE
|
|
||||||
.env
|
|
||||||
.prettierrc
|
|
||||||
.next
|
|
||||||
next-env.d.ts
|
|
||||||
out
|
|
@ -1,9 +1,27 @@
|
|||||||
{
|
{
|
||||||
"trailingComma": "es5",
|
"trailingComma": "none",
|
||||||
"useTabs": true,
|
"useTabs": true,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": true,
|
"printWidth": 80,
|
||||||
"tabWidth": 2,
|
"arrowParens": "always",
|
||||||
"printWidth": 100,
|
"importOrderSeparation": true,
|
||||||
"arrowParens": "always"
|
"importOrder": [
|
||||||
|
"@rollup/.*",
|
||||||
|
"rollup-.*",
|
||||||
|
"^..?/.*",
|
||||||
|
"^use-local-storage-state$",
|
||||||
|
"^styled-components$",
|
||||||
|
"^react.*",
|
||||||
|
"^preact$",
|
||||||
|
"^preact.*",
|
||||||
|
"^axios.*",
|
||||||
|
"^@src/.*",
|
||||||
|
"^@models/.*",
|
||||||
|
"^@interfaces/.*",
|
||||||
|
"^@styles/.*",
|
||||||
|
"^@shared/.*",
|
||||||
|
"^@utils/.*",
|
||||||
|
"^@components/.*",
|
||||||
|
"^@svg/.*"
|
||||||
|
]
|
||||||
}
|
}
|
@ -1,34 +0,0 @@
|
|||||||
|
|
||||||
FROM node:12-alpine AS deps
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
COPY package.json yarn.lock ./
|
|
||||||
RUN yarn install --frozen-lockfile
|
|
||||||
|
|
||||||
FROM node:12-alpine AS builder
|
|
||||||
WORKDIR /app
|
|
||||||
COPY . .
|
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1;
|
|
||||||
RUN yarn build && yarn install --production --ignore-scripts --prefer-offline
|
|
||||||
|
|
||||||
FROM node:12-alpine AS runner
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
ENV NODE_ENV production
|
|
||||||
|
|
||||||
RUN addgroup -g 1001 -S nodejs
|
|
||||||
RUN adduser -S nextjs -u 1001
|
|
||||||
|
|
||||||
COPY --from=builder /app/next.config.js ./
|
|
||||||
COPY --from=builder /app/next-i18next.config.js ./
|
|
||||||
COPY --from=builder /app/public ./public
|
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
|
|
||||||
COPY --from=builder /app/node_modules ./node_modules
|
|
||||||
COPY --from=builder /app/package.json ./package.json
|
|
||||||
|
|
||||||
USER nextjs
|
|
||||||
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
CMD ["yarn", "start"]
|
|
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2021 Guus van Meerveld
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
@ -1,6 +1,5 @@
|
|||||||
<h1 align="center">Portfolio</h1>
|
# Portfolio
|
||||||
<p align="center">
|
|
||||||
<img src="https://api.netlify.com/api/v1/badges/abf23579-f44f-432d-9fb6-6822a7a1f1b2/deploy-status">
|
This is my portfolio website to show off all of my projects and provide my socials.
|
||||||
<img src="https://img.shields.io/website-up-down-green-red/https/guusvanmeerveld.dev.svg">
|
|
||||||
<img src="https://img.shields.io/github/license/Guusvanmeerveld/Portfolio.svg">
|
It's build using Preact and Styled-Components and uses Vite as its build tool.
|
||||||
</p>
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
container_name: portfolio
|
|
||||||
build: .
|
|
||||||
env_file: .env
|
|
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Guus' Portfolio</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #000000;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
<noscript>Javascript needs to be enabled to run this application</noscript>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,6 +0,0 @@
|
|||||||
/// <reference types="next" />
|
|
||||||
/// <reference types="next/types/global" />
|
|
||||||
/// <reference types="next/image-types/global" />
|
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
|
@ -1,18 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import('next').NextConfig}
|
|
||||||
*/
|
|
||||||
module.exports = {
|
|
||||||
images: {
|
|
||||||
loader: 'imgix',
|
|
||||||
path: process.env.IMGIX_PATH || 'https://guusvanmeerveld.imgix.net',
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
CDN_ENDPOINT: process.env.CDN_ENDPOINT,
|
|
||||||
},
|
|
||||||
webpack: (config) => {
|
|
||||||
config.module.rules.push({ test: /\.svg$/, use: ['@svgr/webpack'] });
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,36 +1,39 @@
|
|||||||
{
|
{
|
||||||
"name": "portfolio",
|
"name": "portfolio",
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"format": "prettier src --write",
|
||||||
"build": "next build",
|
"dev": "vite",
|
||||||
"export": "next build && next export",
|
"build": "tsc && vite build",
|
||||||
"start": "next start",
|
"preview": "vite preview"
|
||||||
"prettify": "prettier --write .",
|
|
||||||
"lint": "next lint"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.4",
|
"@fontsource/montserrat": "^4.5.11",
|
||||||
"chalk": "^4.1.2",
|
"@fontsource/prompt": "^4.5.8",
|
||||||
"milligram": "^1.4.1",
|
"@tanstack/react-query": "^4.0.10",
|
||||||
"next": "^11.1.2",
|
"@tanstack/react-query-devtools": "^4.0.10",
|
||||||
"next-themes": "^0.0.15",
|
"axios": "^0.27.2",
|
||||||
"react": "^17.0.2",
|
"bootstrap": "^5.2.0",
|
||||||
"react-dom": "^17.0.2",
|
"preact": "^10.9.0",
|
||||||
"react-icons": "^4.2.0",
|
"react-bootstrap": "^2.4.0",
|
||||||
"sass": "^1.42.1",
|
"react-icons": "^4.4.0",
|
||||||
"sharp": "^0.29.1",
|
"react-router-dom": "6",
|
||||||
"swr": "^1.0.1"
|
"react-spring": "^9.5.2",
|
||||||
|
"reset-css": "^5.0.1",
|
||||||
|
"styled-components": "^5.3.5",
|
||||||
|
"use-local-storage-state": "^18.1.0",
|
||||||
|
"vite-plugin-imagemin": "^0.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@svgr/webpack": "^5.5.0",
|
"@preact/preset-vite": "^2.3.0",
|
||||||
"@types/node": "^15.6.1",
|
"@rollup/plugin-alias": "^3.1.9",
|
||||||
"@types/react": "^17.0.6",
|
"@trivago/prettier-plugin-sort-imports": "^3.3.0",
|
||||||
"eslint": "^7.27.0",
|
"@types/styled-components": "^5.1.25",
|
||||||
"eslint-config-next": "11.1.2",
|
"prettier": "^2.7.1",
|
||||||
"eslint-plugin-prettier": "^3.4.0",
|
"typescript": "^4.6.4",
|
||||||
"prettier": "^2.3.0",
|
"vite": "^3.0.0",
|
||||||
"typescript": "^4.4.3"
|
"vite-tsconfig-paths": "^3.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 7.0 KiB |
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "Porfolio",
|
|
||||||
"name": "Guus van Meerveld's portfolio",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "/favicon.ico",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"background_color": "#FFFFFF",
|
|
||||||
"display": "standalone",
|
|
||||||
"scope": "/",
|
|
||||||
"theme_color": "#388e3c",
|
|
||||||
"description": "A simple portfolio website to display my projects."
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Disallow:
|
|
After Width: | Height: | Size: 89 KiB |
@ -0,0 +1,24 @@
|
|||||||
|
import { createGlobalStyle } from "styled-components";
|
||||||
|
|
||||||
|
const Baseline = createGlobalStyle`
|
||||||
|
body {
|
||||||
|
color: ${({ theme }) => theme.palette.text.primary};
|
||||||
|
background-color: ${({ theme }) => theme.palette.background.primary};
|
||||||
|
font-family: "Montserrat", sans-serif;
|
||||||
|
|
||||||
|
background-image: url("/topography.svg");
|
||||||
|
background-repeat: repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: ${({ theme }) => theme.palette.text.primary};
|
||||||
|
text-decoration: none;
|
||||||
|
:hover {
|
||||||
|
color: ${({ theme }) => theme.palette.text.secondary};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CssBaseline = Baseline as () => JSX.Element;
|
||||||
|
|
||||||
|
export default CssBaseline;
|
@ -0,0 +1,290 @@
|
|||||||
|
import styled, { useTheme } from "styled-components";
|
||||||
|
|
||||||
|
import { FunctionalComponent } from "preact";
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
transform: ${(props) => (props.left ? "scale(-1, -1)" : "none")};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Dots: FunctionalComponent<{
|
||||||
|
left?: boolean;
|
||||||
|
}> = ({ left }) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container left={left}>
|
||||||
|
<svg
|
||||||
|
version="1.0"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 645.000000 456.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
transform="translate(0.000000,456.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill={theme.palette.secondary}
|
||||||
|
stroke="none"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3825 4548 c-28 -16 -35 -28 -35 -64 0 -79 106 -88 133 -11 9 25 -18
|
||||||
|
73 -45 81 -29 7 -29 7 -53 -6z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4455 4548 c-28 -16 -35 -28 -35 -64 0 -39 27 -64 68 -64 58 0 90 76
|
||||||
|
49 117 -19 18 -60 24 -82 11z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5077 4540 c-20 -16 -27 -30 -27 -55 0 -64 80 -89 120 -38 50 64 -29
|
||||||
|
143 -93 93z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5706 4539 c-50 -39 -22 -119 43 -119 20 0 36 9 50 26 27 35 26 59
|
||||||
|
-4 89 -30 30 -54 31 -89 4z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M6336 4539 c-49 -39 -22 -119 42 -119 33 0 45 8 61 37 17 33 14 51
|
||||||
|
-14 78 -30 30 -54 31 -89 4z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3825 3918 c-28 -16 -35 -28 -35 -64 0 -38 27 -64 67 -64 69 0 91
|
||||||
|
100 28 128 -31 14 -35 14 -60 0z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4447 3910 c-20 -16 -27 -31 -27 -54 0 -66 79 -91 120 -39 50 64 -29
|
||||||
|
143 -93 93z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5077 3910 c-20 -16 -27 -31 -27 -54 0 -66 79 -91 120 -39 50 64 -29
|
||||||
|
143 -93 93z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5706 3909 c-50 -39 -24 -119 39 -119 78 0 101 103 29 130 -34 13
|
||||||
|
-38 13 -68 -11z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M6336 3909 c-50 -39 -22 -119 43 -119 75 0 95 104 25 130 -34 13 -38
|
||||||
|
13 -68 -11z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3816 3279 c-52 -41 -25 -119 41 -119 69 0 91 100 28 128 -34 16 -38
|
||||||
|
15 -69 -9z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4446 3279 c-32 -25 -36 -77 -7 -103 28 -25 79 -17 104 16 17 23 18
|
||||||
|
31 8 56 -11 26 -46 52 -70 52 -5 0 -20 -9 -35 -21z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5100 3293 c-50 -18 -68 -84 -32 -116 19 -17 68 -22 88 -9 21 13 34
|
||||||
|
52 28 77 -5 20 -51 58 -66 54 -2 0 -10 -3 -18 -6z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5730 3293 c-31 -12 -50 -37 -50 -68 0 -78 111 -91 135 -16 9 28 -12
|
||||||
|
70 -40 81 -15 6 -27 10 -28 9 -1 0 -9 -3 -17 -6z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M6360 3293 c-73 -27 -62 -133 14 -133 42 0 64 17 72 55 9 48 -42 94
|
||||||
|
-86 78z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M1939 2658 c-36 -12 -55 -58 -38 -90 16 -31 28 -38 65 -38 77 0 88
|
||||||
|
107 14 132 -8 3 -27 1 -41 -4z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M2567 2658 c-27 -10 -45 -46 -38 -78 5 -28 34 -50 66 -50 78 0 90
|
||||||
|
107 15 132 -8 3 -27 1 -43 -4z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3210 2662 c-49 -16 -68 -83 -32 -115 25 -22 75 -21 95 1 43 47 -5
|
||||||
|
133 -63 114z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3840 2662 c-49 -15 -68 -83 -32 -115 24 -22 75 -22 95 1 19 22 23
|
||||||
|
40 14 74 -7 27 -49 49 -77 40z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4470 2662 c-30 -10 -50 -36 -50 -67 0 -39 26 -65 66 -65 35 0 47 8
|
||||||
|
63 38 25 47 -29 110 -79 94z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5100 2662 c-30 -10 -50 -36 -50 -67 0 -39 26 -65 66 -65 35 0 47 8
|
||||||
|
63 38 25 47 -29 110 -79 94z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5730 2662 c-30 -10 -50 -36 -50 -67 0 -78 111 -91 135 -16 14 44
|
||||||
|
-40 97 -85 83z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M6360 2662 c-48 -15 -64 -67 -34 -110 32 -46 124 -16 124 41 0 46
|
||||||
|
-47 82 -90 69z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M1924 2022 c-6 -4 -17 -18 -23 -30 -25 -48 23 -104 79 -93 48 9 68
|
||||||
|
82 32 114 -19 17 -68 22 -88 9z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M2547 2013 c-19 -23 -23 -41 -14 -75 7 -28 43 -46 77 -39 48 9 68 82
|
||||||
|
32 114 -24 22 -75 22 -95 0z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3177 2012 c-39 -42 -8 -114 48 -114 57 0 88 78 47 115 -25 22 -75
|
||||||
|
21 -95 -1z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3807 2012 c-33 -37 -14 -104 33 -113 34 -7 70 11 77 39 9 34 5 52
|
||||||
|
-14 75 -21 23 -75 22 -96 -1z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4437 2012 c-33 -37 -14 -104 33 -113 56 -11 104 45 79 93 -6 12 -17
|
||||||
|
26 -23 30 -21 14 -73 8 -89 -10z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5067 2012 c-33 -37 -14 -104 33 -113 56 -11 104 45 79 93 -6 12 -17
|
||||||
|
26 -23 30 -21 14 -73 8 -89 -10z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5697 2012 c-22 -24 -22 -75 1 -95 54 -48 133 -1 116 69 -11 45 -86
|
||||||
|
61 -117 26z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M6326 2008 c-21 -30 -20 -71 2 -91 45 -40 122 -10 122 48 0 59 -91
|
||||||
|
90 -124 43z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M20 1380 c-41 -41 -11 -113 48 -113 62 0 92 60 56 111 -20 29 -76 30
|
||||||
|
-104 2z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M656 1384 c-38 -37 -27 -96 20 -114 67 -26 128 66 76 113 -23 21 -76
|
||||||
|
22 -96 1z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M1294 1392 c-21 -13 -34 -52 -28 -77 7 -30 54 -58 81 -48 53 18 72
|
||||||
|
83 35 116 -19 17 -68 22 -88 9z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M1911 1374 c-24 -31 -25 -35 -10 -67 31 -67 129 -46 129 28 0 64 -79
|
||||||
|
90 -119 39z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M2547 1383 c-33 -38 -22 -94 24 -113 27 -11 33 -11 57 7 33 25 41 76
|
||||||
|
16 104 -22 24 -76 25 -97 2z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3177 1382 c-34 -38 -16 -101 33 -117 20 -6 59 9 72 29 14 21 8 73
|
||||||
|
-10 89 -25 22 -75 21 -95 -1z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3806 1381 c-25 -28 -17 -79 16 -104 64 -48 135 45 81 106 -21 23
|
||||||
|
-75 22 -97 -2z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4437 1382 c-48 -53 12 -140 77 -110 14 6 30 24 37 40 10 25 9 33 -8
|
||||||
|
56 -26 34 -81 42 -106 14z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5067 1382 c-51 -56 15 -142 82 -107 45 23 49 90 7 117 -21 14 -73 8
|
||||||
|
-89 -10z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5697 1382 c-48 -53 11 -138 78 -112 45 17 55 79 19 115 -21 21 -77
|
||||||
|
19 -97 -3z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M6326 1378 c-36 -51 -6 -111 56 -111 36 0 68 32 68 69 0 58 -92 89
|
||||||
|
-124 42z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M26 754 c-40 -40 -24 -105 30 -118 62 -16 110 68 65 113 -25 25 -73
|
||||||
|
27 -95 5z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M656 754 c-61 -61 15 -157 83 -106 53 39 30 122 -34 122 -18 0 -41
|
||||||
|
-7 -49 -16z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M1280 743 c-50 -64 29 -143 93 -93 20 16 27 31 27 54 0 66 -79 91
|
||||||
|
-120 39z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M1910 743 c-50 -64 29 -143 93 -93 20 16 27 31 27 54 0 66 -79 91
|
||||||
|
-120 39z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M2548 753 c-36 -43 -27 -93 22 -113 29 -12 65 -1 82 24 14 21 8 73
|
||||||
|
-10 89 -24 22 -75 22 -94 0z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3177 752 c-34 -38 -16 -101 33 -117 20 -6 59 9 72 29 14 21 8 73
|
||||||
|
-10 89 -25 22 -75 21 -95 -1z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3807 752 c-17 -19 -22 -68 -9 -88 4 -6 18 -17 30 -23 48 -25 104 23
|
||||||
|
93 79 -9 48 -82 68 -114 32z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4436 751 c-25 -27 -18 -79 15 -103 36 -27 76 -15 97 28 15 32 15 36
|
||||||
|
-5 62 -26 35 -81 42 -107 13z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5067 752 c-27 -29 -22 -77 10 -102 64 -50 143 29 93 93 -26 33 -78
|
||||||
|
37 -103 9z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5695 746 c-22 -33 -15 -75 16 -98 68 -51 144 45 83 106 -25 25 -81
|
||||||
|
20 -99 -8z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M6325 746 c-22 -33 -15 -75 16 -98 68 -51 144 45 83 106 -25 25 -81
|
||||||
|
20 -99 -8z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M26 124 c-61 -61 14 -158 82 -107 35 26 41 74 13 102 -25 25 -73 27
|
||||||
|
-95 5z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M651 114 c-12 -15 -21 -35 -21 -46 0 -24 44 -68 68 -68 33 0 72 38
|
||||||
|
72 71 0 65 -80 93 -119 43z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M1280 113 c-50 -64 29 -143 93 -93 20 16 27 30 27 55 0 64 -80 89
|
||||||
|
-120 38z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M1911 114 c-24 -31 -24 -34 -11 -69 26 -69 130 -47 130 29 0 65 -79
|
||||||
|
91 -119 40z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M2548 123 c-25 -29 -29 -53 -13 -82 23 -45 90 -49 117 -7 14 21 8 73
|
||||||
|
-10 89 -24 22 -75 22 -94 0z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3177 122 c-34 -38 -16 -101 33 -117 20 -6 59 9 72 29 14 21 8 73
|
||||||
|
-10 89 -25 22 -75 21 -95 -1z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3807 122 c-17 -19 -22 -68 -9 -88 13 -21 52 -34 77 -28 30 7 58 54
|
||||||
|
48 81 -18 53 -83 72 -116 35z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M4437 122 c-21 -23 -22 -75 -2 -96 38 -38 101 -23 118 28 20 61 -73
|
||||||
|
116 -116 68z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5067 122 c-27 -29 -22 -77 10 -102 64 -50 143 29 93 93 -26 33 -78
|
||||||
|
37 -103 9z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5700 120 c-41 -41 -7 -120 52 -120 24 0 68 44 68 68 0 59 -79 93
|
||||||
|
-120 52z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M6329 119 c-28 -28 -22 -76 13 -102 68 -51 143 46 82 107 -22 22 -70
|
||||||
|
20 -95 -5z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Dots;
|
@ -1,28 +0,0 @@
|
|||||||
@import '../styles/colors.scss';
|
|
||||||
|
|
||||||
.body {
|
|
||||||
background-color: $bg-secondary;
|
|
||||||
border-top: 0.1rem solid $borders;
|
|
||||||
|
|
||||||
padding: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.branding {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 2rem;
|
|
||||||
margin-left: 1rem;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.socials {
|
|
||||||
margin-left: 4rem;
|
|
||||||
display: inline-block;
|
|
||||||
height: 100%;
|
|
||||||
padding: 1rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.socialLink {
|
|
||||||
height: 3rem;
|
|
||||||
width: 3rem;
|
|
||||||
margin-right: 2rem;
|
|
||||||
}
|
|
@ -0,0 +1,10 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
const Header = styled.h1`
|
||||||
|
font-family: "Prompt", sans-serif;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 3rem;
|
||||||
|
margin-bottom: ${(props) => (props.gutter ? "1rem" : "0")};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Header;
|
@ -0,0 +1,11 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
const Image = styled.img.attrs(() => ({ draggable: false }))`
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-o-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Image;
|
@ -1,14 +1,14 @@
|
|||||||
import { FC } from 'react';
|
import { FunctionalComponent } from "preact";
|
||||||
|
|
||||||
import Navbar from '@components/Navbar';
|
import Footer from "@components/Footer";
|
||||||
import Footer from '@components/Footer';
|
|
||||||
|
|
||||||
const Layout: FC<{ children: JSX.Element | JSX.Element[] }> = ({ children }) => (
|
const Layout: FunctionalComponent = ({ children }) => {
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
<Navbar />
|
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Layout;
|
export default Layout;
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
import styled, { DefaultTheme } from "styled-components";
|
||||||
|
|
||||||
|
const Link = styled.a`
|
||||||
|
color: ${({ theme }: { theme: DefaultTheme }) => theme.palette.primary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Link;
|
@ -1,65 +0,0 @@
|
|||||||
@import '../styles/colors.scss';
|
|
||||||
|
|
||||||
.bar {
|
|
||||||
z-index: 1;
|
|
||||||
background-color: $bg-secondary;
|
|
||||||
border-bottom: 0.1rem solid $borders;
|
|
||||||
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
padding: 1.5rem 0;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select {
|
|
||||||
width: 7.5rem;
|
|
||||||
margin-bottom: 0;
|
|
||||||
|
|
||||||
background-color: $bg-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.items {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-size: 1.75rem;
|
|
||||||
margin-left: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.moon,
|
|
||||||
.sun {
|
|
||||||
font-size: 2rem;
|
|
||||||
margin: 0.25rem 1.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.moon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme='dark'] {
|
|
||||||
.sun {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.moon {
|
|
||||||
display: inline-block !important;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
import { useTheme } from 'next-themes';
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import { useTranslation } from 'next-i18next';
|
|
||||||
|
|
||||||
import React, { FC } from 'react';
|
|
||||||
|
|
||||||
import { BiMoon } from 'react-icons/bi';
|
|
||||||
import { ImSun } from 'react-icons/im';
|
|
||||||
|
|
||||||
import styles from './Navbar.module.scss';
|
|
||||||
|
|
||||||
import { ProfileImage } from '@svg/index';
|
|
||||||
|
|
||||||
const Navbar: FC = () => {
|
|
||||||
const { theme, setTheme } = useTheme();
|
|
||||||
const switchTheme = (): void => setTheme(theme == 'dark' ? 'light' : 'dark');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<nav className={styles.bar}>
|
|
||||||
<div className="container">
|
|
||||||
<div className={styles.content}>
|
|
||||||
<div className={styles.header}>
|
|
||||||
<Link href="/">
|
|
||||||
<a>
|
|
||||||
<ProfileImage className="profile" width={32} height={32} />
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className={styles.items}>
|
|
||||||
<Link href="/#projects">
|
|
||||||
<a>Projects</a>
|
|
||||||
</Link>
|
|
||||||
<Link href="/blog">
|
|
||||||
<a>Contact</a>
|
|
||||||
</Link>
|
|
||||||
<Link href="https://github.com/guusvanmeerveld/portfolio">
|
|
||||||
<a>Github</a>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<BiMoon onClick={switchTheme} className={styles.moon} />
|
|
||||||
<ImSun onClick={switchTheme} className={styles.sun} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Navbar;
|
|
@ -1,25 +0,0 @@
|
|||||||
import Head from 'next/head';
|
|
||||||
|
|
||||||
import { FC } from 'react';
|
|
||||||
|
|
||||||
const Page: FC<{
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
children: JSX.Element[] | JSX.Element;
|
|
||||||
}> = ({ title, description, children }) => (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<meta charSet="UTF-8" />
|
|
||||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Guus van Meerveld | {title}</title>
|
|
||||||
<meta name="description" content={description} />
|
|
||||||
<meta name="keywords" content="guus van meerveld argo magister tempo discord" />
|
|
||||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
|
||||||
<link rel="apple-touch-icon" sizes="192x192" href="/favicon.ico" />
|
|
||||||
</Head>
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Page;
|
|
@ -1,21 +0,0 @@
|
|||||||
.body {
|
|
||||||
height: 100vh;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
font-size: 5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import { FC } from 'react';
|
|
||||||
|
|
||||||
import styles from './PageBuilder.module.scss';
|
|
||||||
|
|
||||||
const PageBuilder: FC<{ header: string; subtitle: string; button: string }> = ({
|
|
||||||
header,
|
|
||||||
subtitle,
|
|
||||||
button,
|
|
||||||
children,
|
|
||||||
}) => (
|
|
||||||
<div className={styles.body}>
|
|
||||||
<div>
|
|
||||||
{children}
|
|
||||||
<div className={styles.header}>{header}</div>
|
|
||||||
<div className={styles.subtitle}>{subtitle}</div>
|
|
||||||
<Link href="/">
|
|
||||||
<a className={styles.link + ' button'}>{button}</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default PageBuilder;
|
|
@ -0,0 +1,9 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
const Paragraph = styled.p`
|
||||||
|
font-family: "Montserrat", sans-serif;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: ${(props) => (props.gutter ? ".5rem" : "0")};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Paragraph;
|
@ -1,53 +0,0 @@
|
|||||||
.body {
|
|
||||||
border-top: 0.1rem solid var(--borders);
|
|
||||||
|
|
||||||
padding: 5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info,
|
|
||||||
.right {
|
|
||||||
font-size: 2rem;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:not(:last-child) {
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cover {
|
|
||||||
width: 50%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
|
||||||
.cover,
|
|
||||||
.info,
|
|
||||||
.right {
|
|
||||||
width: 100%;
|
|
||||||
margin: 2rem 0;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
img {
|
|
||||||
height: 15rem;
|
|
||||||
width: 15rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
import Image from 'next/image';
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
import { FC } from 'react';
|
|
||||||
|
|
||||||
import ProjectType from '@models/project';
|
|
||||||
|
|
||||||
import styles from './Project.module.scss';
|
|
||||||
|
|
||||||
interface ProjectComponent extends ProjectType {
|
|
||||||
right: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Project: FC<ProjectComponent> = ({ name, description, buttons, cover, right }) => {
|
|
||||||
return (
|
|
||||||
<div className={styles.body}>
|
|
||||||
<div className={styles.content}>
|
|
||||||
<div className={right ? styles.right : styles.info}>
|
|
||||||
<h2>{name}</h2>
|
|
||||||
<div className={styles.description}>{description}</div>
|
|
||||||
|
|
||||||
{buttons.map((button, i) => (
|
|
||||||
<Link href={button.link} key={i}>
|
|
||||||
<a className={styles.button + ' button'}>{button.text}</a>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{cover ? (
|
|
||||||
<div className={styles.cover}>
|
|
||||||
<Image src={cover} width={200} height={200} alt={name} />
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Project;
|
|
@ -0,0 +1,153 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import styled, { DefaultTheme } from "styled-components";
|
||||||
|
|
||||||
|
import { Spinner } from "react-bootstrap";
|
||||||
|
import Col from "react-bootstrap/Col";
|
||||||
|
import Row from "react-bootstrap/Row";
|
||||||
|
import { AiOutlineStar, AiOutlineFork } from "react-icons/ai";
|
||||||
|
|
||||||
|
import { FunctionalComponent } from "preact";
|
||||||
|
|
||||||
|
import axios, { AxiosError } from "axios";
|
||||||
|
|
||||||
|
import Repo from "@interfaces/repo";
|
||||||
|
|
||||||
|
import Header from "@components/Header";
|
||||||
|
import Link from "@components/Link";
|
||||||
|
import Paragraph from "@components/Paragraph";
|
||||||
|
|
||||||
|
const SpinnerContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectItem = styled(Row)`
|
||||||
|
margin-bottom: 5rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Hero = styled.div`
|
||||||
|
padding: 2rem;
|
||||||
|
background-color: ${({ theme }: { theme: DefaultTheme }) =>
|
||||||
|
theme.palette.background.secondary};
|
||||||
|
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 2px solid
|
||||||
|
${({ theme }: { theme: DefaultTheme }) => theme.palette.border};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Heading = styled(Header)`
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Leading = styled.div`
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Subtitle = styled.h4`
|
||||||
|
color: ${({ theme }: { theme: DefaultTheme }) =>
|
||||||
|
theme.palette.text.secondary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const IconsTray = styled.div`
|
||||||
|
display: flex;
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const IconItem = styled(Paragraph)`
|
||||||
|
margin-right: 1rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Icon = styled.span`
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
color: ${({ theme }: { theme: DefaultTheme }) => theme.palette.primary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TopicsTray = styled.div`
|
||||||
|
margin-top: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Topic = styled.div`
|
||||||
|
background-color: ${({ theme }: { theme: DefaultTheme }) =>
|
||||||
|
theme.palette.primary};
|
||||||
|
|
||||||
|
border-radius: 1.25rem;
|
||||||
|
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ListItem: FunctionalComponent<{
|
||||||
|
repoFullName: string;
|
||||||
|
rtl?: boolean;
|
||||||
|
}> = ({ repoFullName, rtl }) => {
|
||||||
|
const { data, isLoading, error } = useQuery<Repo, AxiosError>(
|
||||||
|
["repo", repoFullName],
|
||||||
|
() =>
|
||||||
|
axios
|
||||||
|
.get(`https://api.github.com/repos/${repoFullName}`)
|
||||||
|
.then((res) => res.data)
|
||||||
|
);
|
||||||
|
|
||||||
|
const body = [
|
||||||
|
<Col md={8}>
|
||||||
|
<Hero>
|
||||||
|
{isLoading && !data && (
|
||||||
|
<SpinnerContainer>
|
||||||
|
<Spinner animation="border" role="status">
|
||||||
|
<span className="visually-hidden">Loading {repoFullName}</span>
|
||||||
|
</Spinner>
|
||||||
|
</SpinnerContainer>
|
||||||
|
)}
|
||||||
|
{data && !isLoading && (
|
||||||
|
<>
|
||||||
|
<Leading>
|
||||||
|
<Heading>
|
||||||
|
<a href={data.html_url}>{data.full_name}</a>
|
||||||
|
</Heading>
|
||||||
|
{data.homepage && (
|
||||||
|
<Subtitle>
|
||||||
|
Website: <Link href={data.homepage}>{data.homepage}</Link>
|
||||||
|
</Subtitle>
|
||||||
|
)}
|
||||||
|
</Leading>
|
||||||
|
<Paragraph gutter>{data.description}</Paragraph>
|
||||||
|
<IconsTray>
|
||||||
|
<IconItem>
|
||||||
|
{data.stargazers_count}
|
||||||
|
<Icon>
|
||||||
|
<AiOutlineStar />
|
||||||
|
</Icon>
|
||||||
|
</IconItem>
|
||||||
|
<IconItem>
|
||||||
|
{data.forks_count}
|
||||||
|
<Icon>
|
||||||
|
<AiOutlineFork />
|
||||||
|
</Icon>
|
||||||
|
</IconItem>
|
||||||
|
</IconsTray>
|
||||||
|
<TopicsTray>
|
||||||
|
<Paragraph>Tags:</Paragraph>
|
||||||
|
{data.topics.map((topic) => (
|
||||||
|
<Topic>{topic}</Topic>
|
||||||
|
))}
|
||||||
|
</TopicsTray>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Hero>
|
||||||
|
</Col>,
|
||||||
|
<Col md={4}></Col>
|
||||||
|
];
|
||||||
|
|
||||||
|
return <ProjectItem>{rtl ? body.reverse() : body}</ProjectItem>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListItem;
|
@ -0,0 +1,76 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import { Spinner } from "react-bootstrap";
|
||||||
|
|
||||||
|
import { FunctionalComponent } from "preact";
|
||||||
|
|
||||||
|
import axios, { AxiosError } from "axios";
|
||||||
|
|
||||||
|
import PinnedRepo from "@interfaces/pinnedRepo";
|
||||||
|
|
||||||
|
import Paragraph from "@components/Paragraph";
|
||||||
|
import ListItem from "@components/Project/Item";
|
||||||
|
|
||||||
|
const SpinnerContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ErrorText = styled(Paragraph)`
|
||||||
|
text-align: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const List: FunctionalComponent = () => {
|
||||||
|
const { data, isLoading, error } = useQuery<PinnedRepo[], AxiosError>(
|
||||||
|
["pinnedRepos"],
|
||||||
|
() =>
|
||||||
|
axios
|
||||||
|
.get(
|
||||||
|
`https://gh-pinned-repos.egoist.sh/?username=${
|
||||||
|
import.meta.env.VITE_GITHUB_USERNAME
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
.then((res) => res.data)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isLoading)
|
||||||
|
return (
|
||||||
|
<SpinnerContainer>
|
||||||
|
<Spinner animation="border" role="status">
|
||||||
|
<span className="visually-hidden">Loading pinned repos...</span>
|
||||||
|
</Spinner>
|
||||||
|
</SpinnerContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return (
|
||||||
|
<ErrorText>
|
||||||
|
Status {error.response?.status ?? "Unknown"} - {error.response?.data}
|
||||||
|
</ErrorText>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!error && data) {
|
||||||
|
if (Array.isArray(data))
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{data.map((repo, i) => {
|
||||||
|
const repoFullName = `${repo.owner}/${repo.repo}`;
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
key={repoFullName}
|
||||||
|
rtl={!(i % 2)}
|
||||||
|
repoFullName={repoFullName}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
else return <ErrorText>Unknown data returned from api</ErrorText>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default List;
|
@ -0,0 +1,12 @@
|
|||||||
|
export default interface PinnedRepo {
|
||||||
|
owner: string;
|
||||||
|
repo: string;
|
||||||
|
link: string;
|
||||||
|
description: string;
|
||||||
|
image: string;
|
||||||
|
website: string;
|
||||||
|
language: string;
|
||||||
|
languageColor: string;
|
||||||
|
stars: string;
|
||||||
|
forks: number;
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
export default interface Repo {
|
||||||
|
id: number;
|
||||||
|
node_id: string;
|
||||||
|
name: string;
|
||||||
|
full_name: string;
|
||||||
|
private: boolean;
|
||||||
|
owner: Owner;
|
||||||
|
html_url: string;
|
||||||
|
description: string;
|
||||||
|
fork: boolean;
|
||||||
|
url: string;
|
||||||
|
forks_url: string;
|
||||||
|
keys_url: string;
|
||||||
|
collaborators_url: string;
|
||||||
|
teams_url: string;
|
||||||
|
hooks_url: string;
|
||||||
|
issue_events_url: string;
|
||||||
|
events_url: string;
|
||||||
|
assignees_url: string;
|
||||||
|
branches_url: string;
|
||||||
|
tags_url: string;
|
||||||
|
blobs_url: string;
|
||||||
|
git_tags_url: string;
|
||||||
|
git_refs_url: string;
|
||||||
|
trees_url: string;
|
||||||
|
statuses_url: string;
|
||||||
|
languages_url: string;
|
||||||
|
stargazers_url: string;
|
||||||
|
contributors_url: string;
|
||||||
|
subscribers_url: string;
|
||||||
|
subscription_url: string;
|
||||||
|
commits_url: string;
|
||||||
|
git_commits_url: string;
|
||||||
|
comments_url: string;
|
||||||
|
issue_comment_url: string;
|
||||||
|
contents_url: string;
|
||||||
|
compare_url: string;
|
||||||
|
merges_url: string;
|
||||||
|
archive_url: string;
|
||||||
|
downloads_url: string;
|
||||||
|
issues_url: string;
|
||||||
|
pulls_url: string;
|
||||||
|
milestones_url: string;
|
||||||
|
notifications_url: string;
|
||||||
|
labels_url: string;
|
||||||
|
releases_url: string;
|
||||||
|
deployments_url: string;
|
||||||
|
created_at: Date;
|
||||||
|
updated_at: Date;
|
||||||
|
pushed_at: Date;
|
||||||
|
git_url: string;
|
||||||
|
ssh_url: string;
|
||||||
|
clone_url: string;
|
||||||
|
svn_url: string;
|
||||||
|
homepage: string;
|
||||||
|
size: number;
|
||||||
|
stargazers_count: number;
|
||||||
|
watchers_count: number;
|
||||||
|
language: string;
|
||||||
|
has_issues: boolean;
|
||||||
|
has_projects: boolean;
|
||||||
|
has_downloads: boolean;
|
||||||
|
has_wiki: boolean;
|
||||||
|
has_pages: boolean;
|
||||||
|
forks_count: number;
|
||||||
|
mirror_url: null;
|
||||||
|
archived: boolean;
|
||||||
|
disabled: boolean;
|
||||||
|
open_issues_count: number;
|
||||||
|
license: License;
|
||||||
|
allow_forking: boolean;
|
||||||
|
is_template: boolean;
|
||||||
|
web_commit_signoff_required: boolean;
|
||||||
|
topics: string[];
|
||||||
|
visibility: string;
|
||||||
|
forks: number;
|
||||||
|
open_issues: number;
|
||||||
|
watchers: number;
|
||||||
|
default_branch: string;
|
||||||
|
temp_clone_token: null;
|
||||||
|
network_count: number;
|
||||||
|
subscribers_count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface License {
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
spdx_id: string;
|
||||||
|
url: string;
|
||||||
|
node_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Owner {
|
||||||
|
login: string;
|
||||||
|
id: number;
|
||||||
|
node_id: string;
|
||||||
|
avatar_url: string;
|
||||||
|
gravatar_id: string;
|
||||||
|
url: string;
|
||||||
|
html_url: string;
|
||||||
|
followers_url: string;
|
||||||
|
following_url: string;
|
||||||
|
gists_url: string;
|
||||||
|
starred_url: string;
|
||||||
|
subscriptions_url: string;
|
||||||
|
organizations_url: string;
|
||||||
|
repos_url: string;
|
||||||
|
events_url: string;
|
||||||
|
received_events_url: string;
|
||||||
|
type: string;
|
||||||
|
site_admin: boolean;
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
import "@fontsource/montserrat";
|
||||||
|
import "@fontsource/prompt";
|
||||||
|
import "bootstrap/dist/css/bootstrap.min.css";
|
||||||
|
import "reset-css";
|
||||||
|
|
||||||
|
import App from "./pages/app";
|
||||||
|
|
||||||
|
import { render } from "preact";
|
||||||
|
|
||||||
|
render(<App />, document.getElementById("app") as HTMLElement);
|
@ -1,11 +0,0 @@
|
|||||||
export default interface Project {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
cover?: string;
|
|
||||||
buttons: Button[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Button {
|
|
||||||
link: string;
|
|
||||||
text: string;
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
declare module '*svg' {
|
|
||||||
import React from 'react';
|
|
||||||
const SVG: React.VFC<React.SVGProps<SVGSVGElement>>;
|
|
||||||
export default SVG;
|
|
||||||
}
|
|
@ -1,22 +1,5 @@
|
|||||||
import { GetStaticProps, NextPage } from 'next';
|
const NotFound = () => {
|
||||||
import { useTranslation } from 'next-i18next';
|
return <div>Page not found</div>;
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
|
||||||
|
|
||||||
import Page from '@components/Page';
|
|
||||||
import Layout from '@components/Layout';
|
|
||||||
import PageBuilder from '@components/PageBuilder';
|
|
||||||
|
|
||||||
const NotFound: NextPage = () => {
|
|
||||||
const title = 'Page not found';
|
|
||||||
const description = "This page either doesn't exist or has been deleted";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Page title={title} description={description}>
|
|
||||||
<Layout>
|
|
||||||
<PageBuilder button="Go back" header={title} subtitle={description} />
|
|
||||||
</Layout>
|
|
||||||
</Page>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NotFound;
|
export default NotFound;
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
.body {
|
|
||||||
background: var(--secondary);
|
|
||||||
margin-top: 6rem;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 7rem 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile {
|
|
||||||
margin: auto;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
margin-top: 2rem;
|
|
||||||
margin-bottom: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.projectsHeader {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.projects {
|
|
||||||
margin-top: 3rem;
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import { ThemeProvider } from 'next-themes';
|
|
||||||
|
|
||||||
import type { AppProps } from 'next/app';
|
|
||||||
|
|
||||||
import 'milligram';
|
|
||||||
|
|
||||||
import '@styles/montserrat.css';
|
|
||||||
|
|
||||||
import '@styles/globals.scss';
|
|
||||||
|
|
||||||
const App = ({ Component, pageProps }: AppProps): JSX.Element => {
|
|
||||||
return (
|
|
||||||
<ThemeProvider>
|
|
||||||
<Component {...pageProps} />
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
@ -0,0 +1,40 @@
|
|||||||
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||||
|
|
||||||
|
import NotFound from "./404";
|
||||||
|
import Index from "./index";
|
||||||
|
|
||||||
|
import useLocalStorageState from "use-local-storage-state";
|
||||||
|
|
||||||
|
import { ThemeProvider } from "styled-components";
|
||||||
|
|
||||||
|
import { BrowserRouter, Route, Routes } from "react-router-dom";
|
||||||
|
|
||||||
|
import { FunctionalComponent } from "preact";
|
||||||
|
|
||||||
|
import { lightTheme, darkTheme } from "@utils/theme";
|
||||||
|
|
||||||
|
import CssBaseline from "@components/CssBaseline";
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
const App: FunctionalComponent = () => {
|
||||||
|
const [darkMode] = useLocalStorageState("darkMode", { defaultValue: true });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
|
||||||
|
<CssBaseline />
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<ReactQueryDevtools initialIsOpen={false} />
|
||||||
|
<BrowserRouter>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Index />} />
|
||||||
|
<Route path="*" element={<NotFound />} />
|
||||||
|
</Routes>
|
||||||
|
</BrowserRouter>
|
||||||
|
</QueryClientProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
@ -1,85 +1,147 @@
|
|||||||
import { GetStaticProps, NextPage } from 'next';
|
import useLocalStorageState from "use-local-storage-state";
|
||||||
|
|
||||||
import axios, { AxiosError } from 'axios';
|
import styled, { DefaultTheme } from "styled-components";
|
||||||
import chalk from 'chalk';
|
|
||||||
|
import Col from "react-bootstrap/Col";
|
||||||
import { useTranslation } from 'next-i18next';
|
import Container from "react-bootstrap/Container";
|
||||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
import Row from "react-bootstrap/Row";
|
||||||
|
import { animated, useSpring } from "react-spring";
|
||||||
import ProjectModel from '@models/project';
|
|
||||||
|
import { FunctionalComponent } from "preact";
|
||||||
import Layout from '@components/Layout';
|
|
||||||
import Page from '@components/Page';
|
import socials from "@utils/socials";
|
||||||
|
|
||||||
import Project from '@components/Project';
|
import Dots from "@components/Dots";
|
||||||
|
import Header from "@components/Header";
|
||||||
import { ProfileImage } from '@svg/index';
|
import Image from "@components/Image";
|
||||||
|
import Layout from "@components/Layout";
|
||||||
import styles from './Index.module.scss';
|
import Paragraph from "@components/Paragraph";
|
||||||
|
import ProjectsList from "@components/Project/List";
|
||||||
|
|
||||||
|
const Presentation = styled.div`
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Projects = styled.div`
|
||||||
|
margin-top: 5rem;
|
||||||
|
padding: 5rem 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Hero = styled.div`
|
||||||
|
/* background-color: ${({ theme }: { theme: DefaultTheme }) =>
|
||||||
|
theme.palette.background.secondary}; */
|
||||||
|
justify-content: ${(props) => (props.right ? "right" : "left")};
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Logo = styled.div`
|
||||||
|
margin: 2rem 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectsHeader = styled(Header)`
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 5rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const IconsTray = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: right;
|
||||||
|
margin-top: 1rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Icon = styled.div`
|
||||||
|
margin-left: 1.5rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Index: FunctionalComponent = () => {
|
||||||
|
const [darkMode] = useLocalStorageState("darkMode");
|
||||||
|
|
||||||
|
const fadeIn = useSpring({
|
||||||
|
to: { opacity: 1 },
|
||||||
|
from: { opacity: 0 },
|
||||||
|
config: {
|
||||||
|
mass: 5
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const Home: NextPage<{ projects: ProjectModel[] }> = ({ projects }) => {
|
|
||||||
return (
|
return (
|
||||||
<Page description="A simple portfolio website to display my projects." title="Home">
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<div className={styles.body}>
|
<Presentation>
|
||||||
<div className={styles.content + ' container'}>
|
<Container>
|
||||||
<div className={styles.profile}>
|
<animated.div style={fadeIn}>
|
||||||
<ProfileImage height={100} width={100} className="profile" />
|
<Row>
|
||||||
</div>
|
<Col md={8}>
|
||||||
|
<Hero>
|
||||||
<h1>Guus van Meerveld</h1>
|
<div>
|
||||||
<h4 className={styles.subtitle}>Full-stack developer</h4>
|
<Header gutter>Welcome</Header>
|
||||||
|
<Paragraph gutter>
|
||||||
<a href="#projects" className="button">
|
My name is Guus van Meerveld, and I am a web developer.
|
||||||
Check out my projects
|
</Paragraph>
|
||||||
</a>
|
<Paragraph>
|
||||||
</div>
|
This is my portfolio website, to showcase my projects.
|
||||||
</div>
|
</Paragraph>
|
||||||
|
|
||||||
<div className={styles.projects}>
|
|
||||||
<div className="container">
|
|
||||||
<h1 className={styles.projectsHeader} id="projects">
|
|
||||||
Projects
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{projects.map((project, i) => {
|
|
||||||
const props = { ...project, right: (i + 1) % 2 == 0 };
|
|
||||||
return <Project key={project.name} {...props} />;
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
|
</Hero>
|
||||||
|
</Col>
|
||||||
|
<Col md={4}>
|
||||||
|
<Dots />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Logo>
|
||||||
|
<Image
|
||||||
|
// onClick={() => setDarkMode(!darkMode)}
|
||||||
|
// style={{ cursor: "pointer" }}
|
||||||
|
height={64}
|
||||||
|
src={darkMode ? "logo-dark.png" : "logo-light.png"}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</Logo>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col md={4}>
|
||||||
|
<Dots left />
|
||||||
|
</Col>
|
||||||
|
<Col md={8}>
|
||||||
|
<Hero right>
|
||||||
|
<div>
|
||||||
|
<Header>Follow me:</Header>
|
||||||
|
<IconsTray>
|
||||||
|
{socials.map((social) => (
|
||||||
|
<Icon>
|
||||||
|
<a href={social.url}>{social.icon}</a>
|
||||||
|
</Icon>
|
||||||
|
))}
|
||||||
|
</IconsTray>
|
||||||
</div>
|
</div>
|
||||||
|
</Hero>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</animated.div>
|
||||||
|
</Container>
|
||||||
|
</Presentation>
|
||||||
|
|
||||||
|
<Projects>
|
||||||
|
<Container>
|
||||||
|
<Row>
|
||||||
|
<ProjectsHeader>Projects</ProjectsHeader>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<ProjectsList />
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
</Projects>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Page>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async () => {
|
|
||||||
const projects: ProjectModel[] | undefined = await axios
|
|
||||||
.get(`https://${process.env.CDN_ENDPOINT}/portfolio/projects-en.json`)
|
|
||||||
.then(({ data }) => {
|
|
||||||
console.log(
|
|
||||||
chalk`{magenta event} - retrieved projects from ` +
|
|
||||||
process.env.CDN_ENDPOINT +
|
|
||||||
' successfully'
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return data;
|
|
||||||
})
|
|
||||||
.catch((error: AxiosError) => {
|
|
||||||
console.log(chalk`{red error} - failed to retrieve projects:`);
|
|
||||||
|
|
||||||
console.log(error.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (projects) {
|
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
projects,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error('Failed to retrieve projects from ' + process.env.CDN_ENDPOINT);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Home;
|
export default Index;
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
import JSX = preact.JSX;
|
@ -0,0 +1,21 @@
|
|||||||
|
import Theme from "./interfaces/theme";
|
||||||
|
|
||||||
|
import "styled-components";
|
||||||
|
|
||||||
|
declare module "styled-components" {
|
||||||
|
export interface DefaultTheme {
|
||||||
|
palette: {
|
||||||
|
primary: string;
|
||||||
|
secondary: string;
|
||||||
|
background: {
|
||||||
|
primary: string;
|
||||||
|
secondary: string;
|
||||||
|
};
|
||||||
|
text: {
|
||||||
|
primary: string;
|
||||||
|
secondary: string;
|
||||||
|
};
|
||||||
|
border: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
$primary: var(--primary);
|
|
||||||
$bg-primary: var(--background);
|
|
||||||
$bg-secondary: var(--secondary);
|
|
||||||
$borders: var(--borders);
|
|
||||||
$text: var(--text);
|
|
Binary file not shown.
@ -1,7 +0,0 @@
|
|||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
background-color: $bg-primary;
|
|
||||||
color: $text;
|
|
||||||
|
|
||||||
font-family: 'Montserrat';
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
.button {
|
|
||||||
background-color: var(--primary);
|
|
||||||
border-color: var(--primary);
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
input,
|
|
||||||
textarea,
|
|
||||||
select {
|
|
||||||
&:focus {
|
|
||||||
border-color: $primary !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
color: $text;
|
|
||||||
border-color: $borders !important;
|
|
||||||
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
&:focus {
|
|
||||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 8" width="30"><path fill="%23388e3c" d="M0,0l6,8l6-8"/></svg>');
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
a {
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
td,
|
|
||||||
th {
|
|
||||||
border-color: var(--borders);
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
:root {
|
|
||||||
--primary: #9ccc65;
|
|
||||||
--secondary: #f4f5f6;
|
|
||||||
--background: white;
|
|
||||||
--borders: #e4e4e4;
|
|
||||||
--text: #606c76;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme='dark'] {
|
|
||||||
.profile {
|
|
||||||
filter: invert(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
--primary: #388e3c;
|
|
||||||
--secondary: #1c1c1c;
|
|
||||||
--background: #212123;
|
|
||||||
--borders: #3c3838;
|
|
||||||
--text: rgb(236, 235, 235);
|
|
||||||
}
|
|
||||||
|
|
||||||
@import './colors.scss';
|
|
||||||
|
|
||||||
@import './global/body.scss';
|
|
||||||
@import './global/link.scss';
|
|
||||||
@import './global/input.scss';
|
|
||||||
@import './global/table.scss';
|
|
||||||
@import './global/button.scss';
|
|
@ -1,10 +0,0 @@
|
|||||||
/* latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Montserrat';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: url('./fonts/montserrat.woff2') format('woff2');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F,
|
|
||||||
U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export { default as ProfileImage } from './profile.svg';
|
|
Before Width: | Height: | Size: 629 B |
@ -0,0 +1,34 @@
|
|||||||
|
import { BsTwitter, BsYoutube, BsMastodon, BsGithub } from "react-icons/bs";
|
||||||
|
import { SiKofi } from "react-icons/si";
|
||||||
|
|
||||||
|
const socials: { name: string; url: string; icon: JSX.Element }[] = [
|
||||||
|
{
|
||||||
|
name: "Twitter",
|
||||||
|
url: `https://twitter.com/${import.meta.env.VITE_TWITTER_USERNAME}`,
|
||||||
|
icon: <BsTwitter />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Youtube",
|
||||||
|
url: `https://youtube.com/channel/${
|
||||||
|
import.meta.env.VITE_YOUTUBE_CHANNEL_ID
|
||||||
|
}`,
|
||||||
|
icon: <BsYoutube />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mastodon",
|
||||||
|
url: import.meta.env.VITE_MASTODON_URL,
|
||||||
|
icon: <BsMastodon />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Ko-fi",
|
||||||
|
url: `https://ko-fi.com/${import.meta.env.VITE_KOFI_USERNAME}`,
|
||||||
|
icon: <SiKofi />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Github",
|
||||||
|
url: `https://github.com/${import.meta.env.VITE_GITHUB_USERNAME}`,
|
||||||
|
icon: <BsGithub />
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default socials;
|
@ -0,0 +1,33 @@
|
|||||||
|
import { DefaultTheme } from "styled-components";
|
||||||
|
|
||||||
|
export const lightTheme: DefaultTheme = {
|
||||||
|
palette: {
|
||||||
|
background: {
|
||||||
|
primary: "#FBF8F1",
|
||||||
|
secondary: "#F7ECDE"
|
||||||
|
},
|
||||||
|
border: "#dcdcdc",
|
||||||
|
primary: "#E9DAC1",
|
||||||
|
secondary: "#54BAB9",
|
||||||
|
text: {
|
||||||
|
primary: "#111111",
|
||||||
|
secondary: "#4d4d4d"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const darkTheme: DefaultTheme = {
|
||||||
|
palette: {
|
||||||
|
background: {
|
||||||
|
primary: "#000000",
|
||||||
|
secondary: "#1d1d1d"
|
||||||
|
},
|
||||||
|
border: "#454545",
|
||||||
|
primary: "#DC0067",
|
||||||
|
secondary: "#00dc75",
|
||||||
|
text: {
|
||||||
|
primary: "#eee",
|
||||||
|
secondary: "#ccc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,11 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_GITHUB_USERNAME: string;
|
||||||
|
readonly VITE_TWITTER_USERNAME: string;
|
||||||
|
readonly VITE_YOUTUBE_CHANNEL_ID: string;
|
||||||
|
readonly VITE_MASTODON_URL: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
@ -1,28 +1,30 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": "src",
|
"target": "ESNext",
|
||||||
"paths": {
|
"useDefineForClassFields": true,
|
||||||
"@components/*": ["components/*"],
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
"@svg/*": ["svg/*"],
|
"allowJs": false,
|
||||||
"@styles/*": ["styles/*"],
|
|
||||||
"@models/*": ["models/*"],
|
|
||||||
"@config/*": ["config/*"],
|
|
||||||
"@src/*": ["./*"]
|
|
||||||
},
|
|
||||||
"target": "es5",
|
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
|
||||||
"allowJs": true,
|
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": false,
|
"esModuleInterop": false,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"noEmit": true,
|
"module": "ESNext",
|
||||||
"esModuleInterop": true,
|
"moduleResolution": "Node",
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve"
|
"noEmit": true,
|
||||||
|
"paths": {
|
||||||
|
"@components/*": ["./src/components/*"],
|
||||||
|
"@interfaces/*": ["./src/interfaces/*"],
|
||||||
|
"@utils/*": ["./src/utils/*"],
|
||||||
|
"react": ["./node_modules/preact/compat/"],
|
||||||
|
"react-dom": ["./node_modules/preact/compat/"]
|
||||||
|
},
|
||||||
|
"jsx": "preserve",
|
||||||
|
"jsxFactory": "h",
|
||||||
|
"jsxFragmentFactory": "Fragment"
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
"include": ["src"],
|
||||||
"exclude": ["node_modules"]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
import preact from "@preact/preset-vite";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
import viteImagemin from "vite-plugin-imagemin";
|
||||||
|
import tsconfigPaths from "vite-tsconfig-paths";
|
||||||
|
|
||||||
|
import alias from "@rollup/plugin-alias";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
preact(),
|
||||||
|
alias({
|
||||||
|
entries: [
|
||||||
|
{ find: "react", replacement: "preact/compat" },
|
||||||
|
{ find: "react-dom/test-utils", replacement: "preact/test-utils" },
|
||||||
|
{ find: "react-dom", replacement: "preact/compat" },
|
||||||
|
{ find: "react/jsx-runtime", replacement: "preact/jsx-runtime" }
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
tsconfigPaths(),
|
||||||
|
viteImagemin({
|
||||||
|
optipng: {
|
||||||
|
optimizationLevel: 7
|
||||||
|
},
|
||||||
|
mozjpeg: {
|
||||||
|
quality: 20
|
||||||
|
},
|
||||||
|
pngquant: {
|
||||||
|
quality: [0.8, 0.9],
|
||||||
|
speed: 4
|
||||||
|
},
|
||||||
|
svgo: {
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: "removeViewBox"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "removeEmptyAttrs",
|
||||||
|
active: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
esbuild: {
|
||||||
|
logOverride: { "this-is-undefined-in-esm": "silent" }
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in new issue