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:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- src/**
|
||||
- package.json
|
||||
- yarn.lock
|
||||
- tsconfig.*
|
||||
- vite.config.ts
|
||||
- .env
|
||||
- index.html
|
||||
- public
|
||||
|
||||
jobs:
|
||||
check:
|
||||
deploy_to_pages:
|
||||
name: Build pages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup checkout
|
||||
- name: Setup
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup NodeJS v12
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12.x'
|
||||
node-version: "16.x"
|
||||
cache: "yarn"
|
||||
|
||||
- name: Install Dependencies
|
||||
- name: Install NPM dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: ESlint check
|
||||
run: yarn lint
|
||||
- name: Build client
|
||||
run: yarn run build
|
||||
|
||||
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
|
||||
- name: Deploy to pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: out
|
||||
|
||||
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 }}
|
||||
publish_dir: ./dist
|
||||
cname: guusvanmeerveld.dev
|
||||
|
@ -1,34 +1,24 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# 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,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"printWidth": 100,
|
||||
"arrowParens": "always"
|
||||
"printWidth": 80,
|
||||
"arrowParens": "always",
|
||||
"importOrderSeparation": true,
|
||||
"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>
|
||||
<p align="center">
|
||||
<img src="https://api.netlify.com/api/v1/badges/abf23579-f44f-432d-9fb6-6822a7a1f1b2/deploy-status">
|
||||
<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">
|
||||
</p>
|
||||
# Portfolio
|
||||
|
||||
This is my portfolio website to show off all of my projects and provide my socials.
|
||||
|
||||
It's build using Preact and Styled-Components and uses Vite as its build tool.
|
||||
|
@ -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",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"export": "next build && next export",
|
||||
"start": "next start",
|
||||
"prettify": "prettier --write .",
|
||||
"lint": "next lint"
|
||||
"format": "prettier src --write",
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.4",
|
||||
"chalk": "^4.1.2",
|
||||
"milligram": "^1.4.1",
|
||||
"next": "^11.1.2",
|
||||
"next-themes": "^0.0.15",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-icons": "^4.2.0",
|
||||
"sass": "^1.42.1",
|
||||
"sharp": "^0.29.1",
|
||||
"swr": "^1.0.1"
|
||||
"@fontsource/montserrat": "^4.5.11",
|
||||
"@fontsource/prompt": "^4.5.8",
|
||||
"@tanstack/react-query": "^4.0.10",
|
||||
"@tanstack/react-query-devtools": "^4.0.10",
|
||||
"axios": "^0.27.2",
|
||||
"bootstrap": "^5.2.0",
|
||||
"preact": "^10.9.0",
|
||||
"react-bootstrap": "^2.4.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-router-dom": "6",
|
||||
"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": {
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@types/node": "^15.6.1",
|
||||
"@types/react": "^17.0.6",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-next": "11.1.2",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"prettier": "^2.3.0",
|
||||
"typescript": "^4.4.3"
|
||||
"@preact/preset-vite": "^2.3.0",
|
||||
"@rollup/plugin-alias": "^3.1.9",
|
||||
"@trivago/prettier-plugin-sort-imports": "^3.3.0",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"prettier": "^2.7.1",
|
||||
"typescript": "^4.6.4",
|
||||
"vite": "^3.0.0",
|
||||
"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}
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
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';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
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>
|
||||
);
|
||||
const NotFound = () => {
|
||||
return <div>Page not found</div>;
|
||||
};
|
||||
|
||||
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 axios, { AxiosError } from 'axios';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
|
||||
|
||||
import ProjectModel from '@models/project';
|
||||
|
||||
import Layout from '@components/Layout';
|
||||
import Page from '@components/Page';
|
||||
|
||||
import Project from '@components/Project';
|
||||
|
||||
import { ProfileImage } from '@svg/index';
|
||||
|
||||
import styles from './Index.module.scss';
|
||||
import useLocalStorageState from "use-local-storage-state";
|
||||
|
||||
import styled, { DefaultTheme } from "styled-components";
|
||||
|
||||
import Col from "react-bootstrap/Col";
|
||||
import Container from "react-bootstrap/Container";
|
||||
import Row from "react-bootstrap/Row";
|
||||
import { animated, useSpring } from "react-spring";
|
||||
|
||||
import { FunctionalComponent } from "preact";
|
||||
|
||||
import socials from "@utils/socials";
|
||||
|
||||
import Dots from "@components/Dots";
|
||||
import Header from "@components/Header";
|
||||
import Image from "@components/Image";
|
||||
import Layout from "@components/Layout";
|
||||
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 (
|
||||
<Page description="A simple portfolio website to display my projects." title="Home">
|
||||
<Layout>
|
||||
<div className={styles.body}>
|
||||
<div className={styles.content + ' container'}>
|
||||
<div className={styles.profile}>
|
||||
<ProfileImage height={100} width={100} className="profile" />
|
||||
</div>
|
||||
|
||||
<h1>Guus van Meerveld</h1>
|
||||
<h4 className={styles.subtitle}>Full-stack developer</h4>
|
||||
|
||||
<a href="#projects" className="button">
|
||||
Check out my projects
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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} />;
|
||||
})}
|
||||
<Presentation>
|
||||
<Container>
|
||||
<animated.div style={fadeIn}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<Hero>
|
||||
<div>
|
||||
<Header gutter>Welcome</Header>
|
||||
<Paragraph gutter>
|
||||
My name is Guus van Meerveld, and I am a web developer.
|
||||
</Paragraph>
|
||||
<Paragraph>
|
||||
This is my portfolio website, to showcase my projects.
|
||||
</Paragraph>
|
||||
</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>
|
||||
</Hero>
|
||||
</Col>
|
||||
</Row>
|
||||
</animated.div>
|
||||
</Container>
|
||||
</Presentation>
|
||||
|
||||
<Projects>
|
||||
<Container>
|
||||
<Row>
|
||||
<ProjectsHeader>Projects</ProjectsHeader>
|
||||
</Row>
|
||||
<Row>
|
||||
<ProjectsList />
|
||||
</Row>
|
||||
</Container>
|
||||
</Projects>
|
||||
</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": {
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@components/*": ["components/*"],
|
||||
"@svg/*": ["svg/*"],
|
||||
"@styles/*": ["styles/*"],
|
||||
"@models/*": ["models/*"],
|
||||
"@config/*": ["config/*"],
|
||||
"@src/*": ["./*"]
|
||||
},
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": 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"],
|
||||
"exclude": ["node_modules"]
|
||||
"include": ["src"],
|
||||
"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