refactor(v3): Initial revamp

dependabot/npm_and_yarn/typescript-eslint/parser-4.33.0
Guus van Meerveld 1 year ago
parent d85095095d
commit e062b44e1c
Signed by: Guusvanmeerveld
GPG Key ID: 2BA7D7912771966E

@ -0,0 +1,8 @@
**
!next.config.js
!tsconfig.json
!package.json
!yarn.lock
!public
!src

@ -1,5 +1 @@
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
NEXT_PUBLIC_GITHUB_USERNAME=Guusvanmeerveld

@ -0,0 +1,67 @@
{
"root": true,
"env": {
"node": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 8
},
"plugins": [
"prettier",
"css-modules"
],
"ignorePatterns": [
"node_modules/*",
".next/*",
".out/*"
],
"extends": [
"eslint:recommended",
"plugin:css-modules/recommended"
],
"overrides": [
{
"files": [
"**/*.ts",
"**/*.tsx"
],
"parser": "@typescript-eslint/parser",
"settings": {
"react": {
"version": "detect"
}
},
"env": {
"browser": true,
"node": true,
"es6": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended"
],
"rules": {
"prettier/prettier": "error",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"jsx-a11y/anchor-is-valid": "off",
"jsx-a11y/no-autofocus": "off",
"@typescript-eslint/no-unused-vars": [
"error"
],
"@typescript-eslint/explicit-function-return-type": [
"warn",
{
"allowExpressions": true,
"allowConciseArrowFunctionExpressionsStartingWithVoid": true,
"allowTypedFunctionExpressions": true
}
]
}
}
]
}

@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

@ -0,0 +1,20 @@
name: Auto-merge dependabot
on:
pull_request:
permissions:
pull-requests: write
jobs:
auto-merge:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
name: Checkout
- uses: ahmadnassri/action-dependabot-auto-merge@v2
name: Auto merge
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

@ -0,0 +1,45 @@
name: deploy
on:
push:
branches:
- master
jobs:
typescript:
runs-on: ubuntu-latest
steps:
- name: Setup checkout
uses: actions/checkout@v2
- name: Setup NodeJS v12
uses: actions/setup-node@v1
with:
node-version: 16
- name: Install Dependencies
run: yarn install
- name: Check for syntax errors
run: yarn test-build
- name: ESlint check
run: yarn lint
docker:
runs-on: ubuntu-latest
needs: typescript
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build Dockerfile and push
id: docker_build
uses: docker/build-push-action@v2
with:
push: true
tags: guusvanmeerveld/portfolio:latest
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

@ -1,42 +0,0 @@
name: "Deploy website"
on:
push:
branches:
- main
paths:
- src/**
- package.json
- yarn.lock
- tsconfig.*
- vite.config.ts
- .env
- index.html
- public
jobs:
deploy_to_pages:
name: Build pages
runs-on: ubuntu-latest
steps:
- name: Setup
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: "16.x"
cache: "yarn"
- name: Install NPM dependencies
run: yarn install
- name: Build client
run: yarn run build
- name: Deploy to pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
cname: guusvanmeerveld.dev

26
.gitignore vendored

@ -1,24 +1,4 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
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?
.env.local
.next
yarn-error.log

@ -1,27 +0,0 @@
{
"trailingComma": "none",
"useTabs": true,
"semi": true,
"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/.*"
]
}

@ -0,0 +1,7 @@
{
"trailingComma": "none",
"useTabs": true,
"semi": true,
"printWidth": 80,
"arrowParens": "always"
}

@ -0,0 +1,9 @@
{
"extends": [
"stylelint-config-standard",
"stylelint-config-idiomatic-order"
],
"rules": {
"indentation": "tab"
}
}

@ -1,3 +0,0 @@
{
"prettier.configPath": ".prettierrc"
}

@ -0,0 +1,32 @@
FROM node:alpine AS deps
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
FROM node: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: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/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,5 +0,0 @@
# 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.

@ -0,0 +1,8 @@
version: "3"
services:
app:
build: .
container_name: app
ports:
- 3000:3000

@ -1,20 +0,0 @@
<!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>

5
next-env.d.ts vendored

@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

@ -0,0 +1,7 @@
// @ts-check
/**
* @type {import('next/dist/next-server/server/config').NextConfig}
**/
module.exports = {
reactStrictMode: true,
}

@ -1,39 +1,42 @@
{
"name": "portfolio",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"format": "prettier src --write",
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@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": {
"@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"
}
"license": "MIT",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"export": "next build && next export",
"test-build": "tsc",
"lint": "eslint src",
"stylelint": "npx stylelint **/*.scss",
"full-test": "yarn test-build && yarn lint && yarn stylelint"
},
"dependencies": {
"axios": "^1.1.2",
"configcat-node": "^8.0.0",
"next": "^12.1.0",
"next-seo": "^4.24.0",
"next-themes": "^0.2.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"spectre.css": "^0.5.9"
},
"devDependencies": {
"@types/node": "^15.12.1",
"@types/react": "^17.0.9",
"@types/react-dom": "^17.0.6",
"@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0",
"eslint": "^7.28.0",
"eslint-plugin-css-modules": "^2.11.0",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"prettier": "^2.3.1",
"sass": "^1.34.1",
"stylelint": "^13.13.1",
"stylelint-config-idiomatic-order": "^8.1.0",
"stylelint-config-standard": "^22.0.0",
"typescript": "^4.3.2"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 89 KiB

@ -0,0 +1,40 @@
import Link from "next/link";
import { FC } from "react";
import { BestRepository } from "@interfaces/repository";
const BestRepository: FC<{ repository: BestRepository }> = ({ repository }) => {
return (
<div className="hero bg-primary">
<div className="container">
<div className="columns">
<div className="column col-8 col-mx-auto">
<h3 className="text-secondary">My most popular project:</h3>
<h1>{repository.name}</h1>
<h3 className="text-secondary">
{repository.stargazers_count} Star(s)
</h3>
<h5>{repository.description}</h5>
<p className="text-secondary">
Written in {repository.language}, has{" "}
{repository.open_issues_count} issue(s) and{" "}
{repository.forks_count} fork(s).
</p>
<Link href={repository.url}>
<a className="btn mr-2">Github</a>
</Link>
{repository.homepage && (
<Link href={repository.homepage}>
<a className="btn">Website</a>
</Link>
)}
</div>
</div>
</div>
</div>
);
};
export default BestRepository;

@ -1,24 +0,0 @@
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;

@ -1,290 +0,0 @@
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,61 +1,51 @@
import styled, { DefaultTheme } from "styled-components";
import { FC } from "react";
import Col from "react-bootstrap/Col";
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import Link from "next/link";
import { FunctionalComponent } from "preact";
import multipleClassNames from "@utils/multipleClassNames";
import socials from "@utils/socials";
import styles from "./footer.module.scss";
import Header from "@components/Header";
const Body = styled.div`
background-color: ${({ theme }: { theme: DefaultTheme }) =>
theme.palette.background.secondary};
border-top: 2px solid
${({ theme }: { theme: DefaultTheme }) => theme.palette.border};
padding: 2rem;
`;
const ListHeader = styled(Header)`
font-size: 2rem;
`;
const ListItem = styled.li`
font-size: 1.25rem;
margin-top: 1rem;
`;
const ListItemIcon = styled.span`
margin-right: 1rem;
`;
const Footer: FunctionalComponent = () => {
const Footer: FC = () => {
return (
<Body>
<Container>
<Row>
<Col md={4}>
<Header gutter>Guus van Meerveld</Header>
</Col>
<Col md={4}>
<ul>
<ListHeader>Socials</ListHeader>
{socials.map((social) => (
<ListItem>
<a href={social.url}>
<ListItemIcon>{social.icon}</ListItemIcon>
{social.name}
</a>
</ListItem>
))}
</ul>
</Col>
</Row>
</Container>
</Body>
<footer className={multipleClassNames("container", styles.main)}>
<div className="columns">
<div className="column col-8 col-mx-auto">
<h3>Guus van Meerveld</h3>
<div className="columns mb-2">
<div className="column col-12">
<Link
href={`https://github.com/${process.env.NEXT_PUBLIC_GITHUB_USERNAME}`}
>
<a className="mr-2">Github</a>
</Link>
&middot;
<Link href="https://twitter.com/Guusvanmeerveld">
<a className="mx-2">Twitter</a>
</Link>
&middot;
<Link href="https://ko-fi.com/Guusvanmeerveld">
<a className="mx-2">Ko-fi</a>
</Link>
&middot;
<Link href="https://youtube.com/channel/UCYuqpoMay5SezCBrA_HKVWQ">
<a className="mx-2">Youtube</a>
</Link>
</div>
</div>
<p>
Built with{" "}
<span role="img" aria-label="heart emoji">
</span>{" "}
by Guus van Meerveld, using{" "}
<Link href="https://picturepan2.github.io/spectre">
<a>Spectre.css</a>
</Link>
</p>
</div>
</div>
</footer>
);
};

@ -1,10 +0,0 @@
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;

@ -1,11 +0,0 @@
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;

@ -0,0 +1,46 @@
import { FC } from "react";
import styles from "./intro.module.scss";
const Intro: FC<{ isAvailable: boolean }> = ({ isAvailable }) => {
return (
<div className={styles.main}>
<div className="container">
<div className="columns">
<div className="column col-8 col-mx-auto text-center">
<h1>Guus van Meerveld</h1>
<h3>
Open source <u>web developer</u>
</h3>
<p>
<a
target="_blank"
rel="noreferrer"
href={`https://github.com/${process.env.NEXT_PUBLIC_GITHUB_USERNAME}`}
className="btn btn-primary mr-2"
>
Github
</a>
<a
href="mailto:contact@guusvanmeerveld.dev"
className="btn btn-primary"
>
Contact
</a>
</p>
<p className="text-gray">
Availibility: {isAvailable && "Available"}
{!isAvailable && "Not available"}
</p>
</div>
</div>
</div>
</div>
);
};
export default Intro;

@ -1,10 +1,12 @@
import { FunctionalComponent } from "preact";
import { FC } from "react";
import Footer from "@components/Footer";
import ThemeChanger from "@components/ThemeChanger";
const Layout: FunctionalComponent = ({ children }) => {
const Layout: FC = ({ children }) => {
return (
<>
<ThemeChanger />
{children}
<Footer />
</>

@ -1,7 +0,0 @@
import styled, { DefaultTheme } from "styled-components";
const Link = styled.a`
color: ${({ theme }: { theme: DefaultTheme }) => theme.palette.primary};
`;
export default Link;

@ -1,9 +0,0 @@
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,153 +0,0 @@
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;

@ -1,76 +0,0 @@
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,66 @@
import { FC } from "react";
import Link from "next/link";
import { RecentRepository } from "@interfaces/repository";
import multipleClassNames from "@utils/multipleClassNames";
import styles from "./repositories.module.scss";
const RecentRepositories: FC<{ repositories: RecentRepository[] }> = ({
repositories
}) => {
return (
<div className={multipleClassNames("container", styles.main)}>
<div className="columns">
<div className="column col-6 col-mx-auto text-center">
<h3>Some of my recent projects:</h3>
</div>
</div>
<div className="columns">
<div className="column col-9 col-mx-auto">
<div className="columns">
{repositories.map((repository) => {
return (
<div
key={repository.name}
className="column col-3 col-md-12 col-mx-auto mb-2"
>
<div
className={multipleClassNames(
"card",
"text-center",
styles.card
)}
>
<div className="card-header text-primary">
<div className="card-title h5">{repository.name}</div>
<div className="card-subtitle text-gray">
{repository.stargazers_count} Star(s)
</div>
</div>
<div className="card-body">{repository.description}</div>
<div className="card-footer">
<Link href={repository.url}>
<a className="btn btn-primary">Github</a>
</Link>
{repository.homepage && (
<Link href={repository.homepage}>
<a className="btn btn-primary ml-2">Website</a>
</Link>
)}
</div>
</div>
</div>
);
})}
</div>
</div>
</div>
</div>
);
};
export default RecentRepositories;

@ -0,0 +1,30 @@
import { FC, useEffect, useState } from "react";
import { useTheme } from "next-themes";
import styles from "./themeChanger.module.scss";
const ThemeChanger: FC = () => {
const [mounted, setMounted] = useState(false);
const { theme, setTheme } = useTheme();
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) return <></>;
return (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
<a
role="button"
tabIndex={0}
className={styles.main}
onClick={() => setTheme(theme == "light" ? "dark" : "light")}
>
{theme}
</a>
);
};
export default ThemeChanger;

@ -0,0 +1,6 @@
$margin: 1rem;
.main {
margin-top: $margin;
margin-bottom: $margin;
}

@ -0,0 +1,3 @@
.main {
padding-top: 10rem;
}

@ -0,0 +1,7 @@
.card {
border: 0;
}
.main {
margin-bottom: 3rem;
}

@ -0,0 +1,7 @@
.main {
position: absolute;
top: 1rem;
right: 1rem;
cursor: pointer;
}

@ -1,12 +0,0 @@
export default interface PinnedRepo {
owner: string;
repo: string;
link: string;
description: string;
image: string;
website: string;
language: string;
languageColor: string;
stars: string;
forks: number;
}

@ -1,4 +1,4 @@
export default interface Repo {
export interface GithubAPIRepository {
id: number;
node_id: string;
name: string;
@ -6,7 +6,7 @@ export default interface Repo {
private: boolean;
owner: Owner;
html_url: string;
description: string;
description: null | string;
fork: boolean;
url: string;
forks_url: string;
@ -52,11 +52,11 @@ export default interface Repo {
ssh_url: string;
clone_url: string;
svn_url: string;
homepage: string;
homepage: null | string;
size: number;
stargazers_count: number;
watchers_count: number;
language: string;
language: null | string;
has_issues: boolean;
has_projects: boolean;
has_downloads: boolean;
@ -67,7 +67,7 @@ export default interface Repo {
archived: boolean;
disabled: boolean;
open_issues_count: number;
license: License;
license: License | null;
allow_forking: boolean;
is_template: boolean;
web_commit_signoff_required: boolean;
@ -77,9 +77,6 @@ export default interface Repo {
open_issues: number;
watchers: number;
default_branch: string;
temp_clone_token: null;
network_count: number;
subscribers_count: number;
}
export interface License {
@ -110,3 +107,18 @@ export interface Owner {
type: string;
site_admin: boolean;
}
export interface RecentRepository {
name: string;
description: string;
url: string;
homepage?: string;
stargazers_count: number;
}
export interface BestRepository extends RecentRepository {
forks_count: number;
language: string;
open_issues_count: number;
pushed_at: Date;
}

@ -1,10 +0,0 @@
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);

@ -0,0 +1,9 @@
import type { DefaultSeoProps } from "next-seo";
const SEO: DefaultSeoProps = {
titleTemplate: "%s | Guus van Meerveld",
defaultTitle: "Guus van Meerveld",
description: "Guus van Meerveld's portfolio"
};
export default SEO;

@ -0,0 +1,8 @@
.main {
height: 100vh;
margin-bottom: 1rem;
display: flex;
justify-content: center;
align-items: center;
}

@ -1,5 +1,35 @@
const NotFound = () => {
return <div>Page not found</div>;
import { NextPage } from "next";
import { useRouter } from "next/router";
import Layout from "@components/Layout";
import multipleClassNames from "@utils/multipleClassNames";
import styles from "./404.module.scss";
const NotFound: NextPage = () => {
const router = useRouter();
return (
<Layout>
<div className={multipleClassNames("empty", styles.main)}>
<div>
<div className="empty-icon">
<i className="icon icon-stop"></i>
</div>
<p className="empty-title h5">Page not found</p>
<p className="empty-subtitle">
The page has either been deleted or moved
</p>
<div className="empty-action">
<button onClick={() => router.back()} className="btn btn-primary">
Go back
</button>
</div>
</div>
</div>
</Layout>
);
};
export default NotFound;

@ -0,0 +1,19 @@
import "@styles/globals.scss";
import SEO from "../next-seo.config";
import { DefaultSeo } from "next-seo";
import { ThemeProvider } from "next-themes";
import type { AppProps } from "next/app";
const App = ({ Component, pageProps }: AppProps): JSX.Element => (
<>
<DefaultSeo {...SEO} />
<ThemeProvider>
<Component {...pageProps} />
</ThemeProvider>
</>
);
export default App;

@ -1,40 +0,0 @@
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,147 +1,75 @@
import useLocalStorageState from "use-local-storage-state";
import { NextSeo } from "next-seo";
import styled, { DefaultTheme } from "styled-components";
import { GetStaticProps, InferGetStaticPropsType, NextPage } from "next";
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 axios from "axios";
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 Intro from "@components/Intro";
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%;
`;
import RecentRepositories from "@components/RecentRepositories";
import BestRepository from "@components/BestRepository";
const ProjectsHeader = styled(Header)`
text-align: center;
margin-bottom: 5rem;
`;
import { GithubAPIRepository } from "@interfaces/repository";
const IconsTray = styled.div`
display: flex;
justify-content: right;
margin-top: 1rem;
`;
import createConfigCatClient from "@utils/createConfigCatClient";
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
}
});
return (
<Layout>
<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>
export const getStaticProps: GetStaticProps = async () => {
const { data } = await axios.get<GithubAPIRepository[]>(
`https://api.github.com/users/${process.env.NEXT_PUBLIC_GITHUB_USERNAME}/repos`
);
const configCatClient = createConfigCatClient();
const isAvailable: boolean =
(await configCatClient?.getValueAsync("amiavailable", true)) ?? true;
const bestRepository = data.sort(
(a, b) => b.stargazers_count - a.stargazers_count
)[0];
return {
props: {
isAvailable,
repositories: data
.sort(
(a, b) =>
new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
)
.map(({ name, description, html_url, stargazers_count, homepage }) => ({
name,
url: html_url,
stargazers_count,
homepage,
description
}))
.slice(0, 3),
bestRepository: {
name: bestRepository.name,
description: bestRepository.description,
url: bestRepository.html_url,
homepage: bestRepository.homepage,
stargazers_count: bestRepository.stargazers_count,
forks_count: bestRepository.forks_count,
language: bestRepository.language,
open_issues_count: bestRepository.open_issues_count,
pushed_at: bestRepository.pushed_at
}
},
revalidate: 60 * 5
};
};
const Index: NextPage = ({
repositories,
isAvailable,
bestRepository
}: InferGetStaticPropsType<typeof getStaticProps>) => (
<Layout>
<NextSeo title="Home" />
<Intro isAvailable={isAvailable} />
<RecentRepositories repositories={repositories} />
<BestRepository repository={bestRepository} />
</Layout>
);
export default Index;

1
src/preact.d.ts vendored

@ -1 +0,0 @@
import JSX = preact.JSX;

21
src/styled.d.ts vendored

@ -1,21 +0,0 @@
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;
};
}
}

@ -0,0 +1,66 @@
[data-theme="dark"] {
body {
background-color: $bg-dark;
color: $text-primary;
}
.empty {
background-color: $dark-color-secondary;
color: $text-primary;
}
.card {
background-color: $bg-dark;
}
a:hover {
color: $primary-color-dark;
}
a {
color: $primary-color;
}
.text-primary {
color: $primary-color !important;
}
.bg-primary {
background-color: $primary-color !important;
}
.btn {
background-color: $bg-dark-secondary;
color: $primary-color;
border-color: $primary-color;
&:hover {
border-color: $primary-color;
}
&:focus {
border-color: $primary-color;
}
}
.btn-primary {
background-color: $primary-color;
border-color: $primary-color;
color: $text-primary;
&:hover {
background-color: $primary-color-dark;
border-color: $primary-color-dark;
}
&:focus {
background-color: $primary-color-dark;
border-color: $primary-color-dark;
}
}
}

@ -0,0 +1,13 @@
$light-color: #eee;
$dark-color: #212121;
$primary-color: #7e57c2;
$dark-color-secondary: lighten($dark-color, 2%);
$primary-color-dark: darken($primary-color, 10%);
$bg-dark: $dark-color;
$bg-dark-secondary: $dark-color-secondary;
$text-primary: $light-color;
$text-secondary: darken($light-color, 10%);

@ -0,0 +1,6 @@
@use "spectre.css/src/spectre.scss";
@use "spectre.css/src/spectre-icons.scss";
@import "variables";
@import "dark";

@ -0,0 +1,21 @@
import * as configcat from "configcat-node";
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const createConfigCatClient = () => {
if (!process.env.CONFIG_CAT_SDK_KEY) return;
const logger = configcat.createConsoleLogger(
process.env.NODE_ENV == "production" ? 0 : 3
);
const configCatClient = configcat.createClient(
process.env.CONFIG_CAT_SDK_KEY,
{
logger
}
);
return configCatClient;
};
export default createConfigCatClient;

@ -0,0 +1,5 @@
const multipleClassNames = (...classNames: string[]): string => {
return classNames.join(" ");
};
export default multipleClassNames;

@ -1,34 +0,0 @@
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;

@ -1,33 +0,0 @@
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"
}
}
};

11
src/vite-env.d.ts vendored

@ -1,11 +0,0 @@
/// <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,30 +1,29 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"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"
"incremental": true,
"tsBuildInfoFile": ".next/tsbuildinfo.json",
"baseUrl": "src",
"paths": {
"@styles/*": ["styles/*"],
"@components/*": ["components/*"],
"@interfaces/*": ["interfaces/*"],
"@utils/*": ["utils/*"],
"@src/*": ["src/*"]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

@ -1,9 +0,0 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

@ -1,48 +0,0 @@
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" }
}
});

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save