Merge pull request #70 from bartvdbraak/feat/3js-tools
Migrate to Svelte
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": ["@commitlint/config-conventional"]
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
ignores: ["@types/node", "@types/react-dom", "autoprefixer", "postcss", "@commitlint/config-conventional", "prettier", "@t3-oss/env-nextjs"]
|
||||
skip-missing: false
|
|
@ -1,10 +0,0 @@
|
|||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
|
@ -1,4 +0,0 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# App
|
||||
# -----------------------------------------------------------------------------
|
||||
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
13
.eslintignore
Normal file
|
@ -0,0 +1,13 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
30
.eslintrc.cjs
Normal file
|
@ -0,0 +1,30 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:svelte/recommended',
|
||||
'prettier'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
parser: 'svelte-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
8
.github/dependabot.yml
vendored
|
@ -1,14 +1,8 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "bartvdbraak"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
reviewers:
|
||||
- "bartvdbraak"
|
||||
- "bartvdbraak"
|
2
renovate.json → .github/renovate.json
vendored
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["config:base"]
|
||||
}
|
||||
}
|
47
.github/workflows/lint-deps-check.yaml
vendored
|
@ -1,47 +0,0 @@
|
|||
name: Linting and Dependency Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
run-checks:
|
||||
name: Run checks
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18, 20]
|
||||
steps:
|
||||
- name: Checkout Git repository
|
||||
uses: actions/checkout@v3.5.3
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2.2.4
|
||||
with:
|
||||
version: 8.6.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3.7.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run linters
|
||||
uses: wearerequired/lint-action@v2.3.0
|
||||
with:
|
||||
eslint: true
|
||||
prettier: true
|
||||
|
||||
- name: Run dependency check
|
||||
run: npx depcheck
|
43
.gitignore
vendored
|
@ -1,35 +1,10 @@
|
|||
# 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
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx --no -- commitlint --edit "$1"
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
npx pretty-quick --staged
|
2
.npmrc
Normal file
|
@ -0,0 +1,2 @@
|
|||
engine-strict=true
|
||||
resolution-mode=highest
|
|
@ -1,8 +1,13 @@
|
|||
cache
|
||||
.cache
|
||||
package.json
|
||||
pnpm-lock.yaml
|
||||
public
|
||||
.DS_Store
|
||||
node_modules
|
||||
.next
|
||||
.github
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
|
9
.prettierrc
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"pluginSearchDirs": ["."],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
8
LICENSE
|
@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
|
|||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
hellob.art -- a simple portfolio journey created using svelte
|
||||
Copyright (C) 2023 Bart van der Braak
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
|
|||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
hellob.art Copyright (C) 2023 Bart van der Braak
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
|
|||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
43
README.md
|
@ -28,18 +28,6 @@ To install the project and its dependencies, follow these steps:
|
|||
pnpm install
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
After setting up the required services, you need to set the corresponding environment variables in the `/.env` file. To do this, follow these steps:
|
||||
|
||||
1. Make a copy of the `.env.example` file:
|
||||
|
||||
```sh-session
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Open the `.env` file in a text editor and populate the values for the services mentioned above.
|
||||
|
||||
## Build
|
||||
|
||||
To build the project, execute the following command:
|
||||
|
@ -54,4 +42,35 @@ To run the project locally, use the following command:
|
|||
|
||||
```sh-session
|
||||
pnpm run dev
|
||||
# or
|
||||
pnpm run dev -- --open
|
||||
```
|
||||
|
||||
## Technologies Used
|
||||
|
||||
- **Svelte:** The framework used for building this portfolio project.
|
||||
- **SvelteKit:** The tooling and routing framework for Svelte projects.
|
||||
- **Tailwind CSS:** A utility-first CSS framework packed with classes.
|
||||
- **Skeleton:** UI Toolkit for Svelte + Tailwind.
|
||||
|
||||
## Deployment
|
||||
|
||||
The portfolio is hosted using [Vercel](https://vercel.com). You can access it at [https://hellob.art](https://hellob.art).
|
||||
|
||||
## Contributing
|
||||
|
||||
I'm open to contributions! If you find any bugs, have suggestions, or want to add something interesting, feel free to open an issue or submit a pull request.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the GPLv3 License. Feel free to explore, learn, and have fun!
|
||||
|
||||
## Get in Touch
|
||||
|
||||
Let's connect! You can find me on:
|
||||
|
||||
- Website: [hellob.art](https://hellob.art)
|
||||
- GitHub: [github.com/bartvdbraak](https://github.com/bartvdbraak)
|
||||
- Email: bart@vanderbraak.nl
|
||||
|
||||
Looking forward to hearing from you! 😊
|
|
@ -1,33 +0,0 @@
|
|||
import Link from "next/link";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
|
||||
interface MarketingLayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default async function MarketingLayout({
|
||||
children,
|
||||
}: MarketingLayoutProps) {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col">
|
||||
<header className="container z-40 bg-background">
|
||||
<div className="flex h-20 items-center justify-between py-6">
|
||||
<nav>
|
||||
<Link
|
||||
href="https://github.com/bartvdbraak"
|
||||
className={cn(
|
||||
buttonVariants({ variant: "secondary", size: "sm" }),
|
||||
"px-4"
|
||||
)}
|
||||
>
|
||||
My GitHub
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<main className="flex-1">{children}</main>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import Link from "next/link";
|
||||
|
||||
import { siteConfig } from "@/config/site";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
|
||||
export default function IndexPage() {
|
||||
return (
|
||||
<>
|
||||
<section className="space-y-6 pb-8 pt-6 md:pb-12 md:pt-10 lg:py-32">
|
||||
<div className="container flex max-w-[64rem] flex-col items-center gap-4 text-center">
|
||||
<Link
|
||||
href={siteConfig.links.linkedIn}
|
||||
className="rounded-2xl bg-muted px-4 py-1.5 text-sm font-medium"
|
||||
target="_blank"
|
||||
>
|
||||
View my LinkedIn profile
|
||||
</Link>
|
||||
<h1 className="font-heading text-3xl sm:text-5xl md:text-6xl lg:text-7xl">
|
||||
hellob.art
|
||||
</h1>
|
||||
<p className="max-w-[42rem] leading-normal text-muted-foreground sm:text-xl sm:leading-8">
|
||||
In progress build of my portfolio using Next.js 13 server
|
||||
components.
|
||||
</p>
|
||||
<div className="space-x-4">
|
||||
<Link href="/login" className={cn(buttonVariants({ size: "lg" }))}>
|
||||
Get Started
|
||||
</Link>
|
||||
<Link
|
||||
href={siteConfig.links.github.profile}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={cn(buttonVariants({ variant: "outline", size: "lg" }))}
|
||||
>
|
||||
GitHub
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
import { Inter as FontSans } from "next/font/google";
|
||||
import localFont from "next/font/local";
|
||||
|
||||
import "@/styles/globals.css";
|
||||
import { siteConfig } from "@/config/site";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Analytics } from "@vercel/analytics/react";
|
||||
import { TailwindIndicator } from "@/components/tailwind-indicator";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
|
||||
export const metadata = {
|
||||
title: {
|
||||
default: siteConfig.name,
|
||||
template: `%s | ${siteConfig.name}`,
|
||||
},
|
||||
metadataBase: new URL(`https://${siteConfig.url}}`),
|
||||
description: siteConfig.description,
|
||||
keywords: [
|
||||
"Portfolio",
|
||||
"Next.js",
|
||||
"React",
|
||||
"Azure",
|
||||
"Kubernetes",
|
||||
"DevOps",
|
||||
"Python",
|
||||
],
|
||||
authors: [
|
||||
{
|
||||
name: "Bart van der Braak",
|
||||
url: siteConfig.url,
|
||||
},
|
||||
],
|
||||
creator: "bartvdbraak",
|
||||
themeColor: [
|
||||
{ media: "(prefers-color-scheme: light)", color: "white" },
|
||||
{ media: "(prefers-color-scheme: dark)", color: "black" },
|
||||
],
|
||||
openGraph: {
|
||||
type: "website",
|
||||
locale: "en_US",
|
||||
url: siteConfig.url,
|
||||
title: siteConfig.name,
|
||||
description: siteConfig.description,
|
||||
siteName: siteConfig.name,
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: siteConfig.name,
|
||||
description: siteConfig.description,
|
||||
images: [`${siteConfig.url}/og.jpg`],
|
||||
creator: "@bartvdbraak",
|
||||
},
|
||||
icons: {
|
||||
icon: "/favicon.ico",
|
||||
shortcut: "/favicon-16x16.png",
|
||||
apple: "/apple-touch-icon.png",
|
||||
},
|
||||
manifest: `${siteConfig.url}/site.webmanifest`,
|
||||
};
|
||||
|
||||
interface RootLayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const fontSans = FontSans({
|
||||
subsets: ["latin"],
|
||||
variable: "--font-sans",
|
||||
});
|
||||
|
||||
const fontHeading = localFont({
|
||||
src: "../assets/fonts/CalSans-SemiBold.woff2",
|
||||
variable: "--font-heading",
|
||||
});
|
||||
|
||||
interface RootLayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }: RootLayoutProps) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<head />
|
||||
<body
|
||||
className={cn(
|
||||
"min-h-screen bg-background font-sans antialiased",
|
||||
fontSans.variable,
|
||||
fontHeading.variable
|
||||
)}
|
||||
>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
{children}
|
||||
<TailwindIndicator />
|
||||
</ThemeProvider>
|
||||
<Analytics />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
export function TailwindIndicator() {
|
||||
if (process.env.NODE_ENV === "production") return null;
|
||||
|
||||
const breakpoints = [
|
||||
{ name: "xs", css: "block sm:hidden" },
|
||||
{
|
||||
name: "sm",
|
||||
css: "hidden sm:block md:hidden lg:hidden xl:hidden 2xl:hidden",
|
||||
},
|
||||
{ name: "md", css: "hidden md:block lg:hidden xl:hidden 2xl:hidden" },
|
||||
{ name: "lg", css: "hidden lg:block xl:hidden 2xl:hidden" },
|
||||
{ name: "xl", css: "hidden xl:block 2xl:hidden" },
|
||||
{ name: "2xl", css: "hidden 2xl:block" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-1 left-1 z-50 flex h-6 w-6 items-center justify-center rounded-full bg-gray-800 p-3 font-mono text-xs text-white">
|
||||
{breakpoints.map((breakpoint) => (
|
||||
<div key={breakpoint.name} className={breakpoint.css}>
|
||||
{breakpoint.name}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
import { ThemeProviderProps } from "next-themes/dist/types";
|
||||
|
||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { VariantProps, cva } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input hover:bg-accent hover:text-accent-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "underline-offset-4 hover:underline text-primary",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 py-2 px-4",
|
||||
sm: "h-9 px-3 rounded-md",
|
||||
lg: "h-11 px-8 rounded-md",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
Button.displayName = "Button";
|
||||
|
||||
export { Button, buttonVariants };
|
|
@ -1,8 +0,0 @@
|
|||
export const navigationConfig = {
|
||||
mainNav: [
|
||||
{
|
||||
title: "Projects",
|
||||
href: "/#projects",
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
import { env } from "@/env.mjs";
|
||||
|
||||
export const siteConfig = {
|
||||
name: "hellob.art",
|
||||
description: "a simple portfolio made by bart van der braak.",
|
||||
url: env.NEXT_PUBLIC_APP_URL || "https://hellob.art",
|
||||
ogImage: "https://hellob.art/og.jpg",
|
||||
links: {
|
||||
linkedIn: "https://linkedin.com/in/bartvdbraak",
|
||||
github: {
|
||||
project: "https://github.com/bartvdbraak/hellob.art",
|
||||
profile: "https://github.com/bartvdbraak",
|
||||
},
|
||||
},
|
||||
};
|
15
env.mjs
|
@ -1,15 +0,0 @@
|
|||
import { createEnv } from "@t3-oss/env-nextjs";
|
||||
import { z } from "zod";
|
||||
|
||||
export const env = createEnv({
|
||||
server: {
|
||||
// This is optional because it's only used in development.
|
||||
// See https://next-auth.js.org/deployment.
|
||||
},
|
||||
client: {
|
||||
NEXT_PUBLIC_APP_URL: z.string().min(1),
|
||||
},
|
||||
runtimeEnv: {
|
||||
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
|
||||
},
|
||||
});
|
21
lib/utils.ts
|
@ -1,21 +0,0 @@
|
|||
import { ClassValue, clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { env } from "@/env.mjs";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function formatDate(input: string | number): string {
|
||||
const date = new Date(input);
|
||||
return date.toLocaleDateString("en-US", {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
export function absoluteUrl(path: string) {
|
||||
return `${env.NEXT_PUBLIC_APP_URL}${path}`;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
84
package.json
|
@ -1,46 +1,42 @@
|
|||
{
|
||||
"name": "hellob.art",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"author": {
|
||||
"name": "Bart van der Braak",
|
||||
"url": "https://hellob.art"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"prepare": "husky install",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"format:write": "prettier . --write --cache",
|
||||
"format:check": "prettier . --check --cache"
|
||||
},
|
||||
"dependencies": {
|
||||
"@t3-oss/env-nextjs": "^0.6.0",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/node": "20.4.2",
|
||||
"@types/react": "18.2.15",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"@vercel/analytics": "^1.0.1",
|
||||
"autoprefixer": "10.4.14",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"eslint": "8.45.0",
|
||||
"eslint-config-next": "13.4.10",
|
||||
"next": "13.4.10",
|
||||
"next-themes": "^0.2.1",
|
||||
"postcss": "8.4.26",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss": "3.3.3",
|
||||
"tailwindcss-animate": "^1.0.6",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/config-conventional": "^17.6.6",
|
||||
"husky": "^8.0.3",
|
||||
"prettier": "2.8.8"
|
||||
}
|
||||
"name": "hellob.art",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||
"format": "prettier --plugin-search-dir . --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@skeletonlabs/skeleton": "^1.10.0",
|
||||
"@sveltejs/adapter-auto": "^2.0.0",
|
||||
"@sveltejs/kit": "^1.20.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-svelte": "^2.30.0",
|
||||
"postcss": "^8.4.24",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier-plugin-svelte": "^2.10.1",
|
||||
"svelte": "^4.0.5",
|
||||
"svelte-check": "^3.4.3",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^4.4.2"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@threlte/core": "6.0.0-next.11",
|
||||
"@threlte/extras": "5.0.0-next.16",
|
||||
"@types/three": "^0.154.0",
|
||||
"three": "^0.155.0"
|
||||
}
|
||||
}
|
||||
|
|
2929
pnpm-lock.yaml
12
postcss.config.cjs
Normal file
|
@ -0,0 +1,12 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const tailwindcss = require('tailwindcss');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
|
||||
const config = {
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
autoprefixer
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = config;
|
|
@ -1,6 +0,0 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 920 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 15 KiB |
|
@ -1,9 +0,0 @@
|
|||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M4 12C5.65685 12 7 10.6569 7 9C7 7.34315 5.65685 6 4 6C2.34315 6 1 7.34315 1 9C1 10.6569 2.34315 12 4 12Z"
|
||||
stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M1 4V12" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M9 1L8.36667 2.93333C8.33424 3.03374 8.27844 3.12501 8.20386 3.19965C8.12928 3.27429 8.03805 3.33016 7.93767 3.36267L6 4L7.93333 4.63333C8.03374 4.66576 8.12501 4.72156 8.19965 4.79614C8.27429 4.87072 8.33016 4.96195 8.36267 5.06233L9 7L9.63333 5.06667C9.66576 4.96626 9.72156 4.87499 9.79614 4.80035C9.87072 4.72571 9.96195 4.66984 10.0623 4.63733L12 4L10.0667 3.36667C9.96626 3.33424 9.87499 3.27844 9.80035 3.20386C9.72571 3.12928 9.66984 3.03805 9.63733 2.93767L9 1Z"
|
||||
stroke="#EAAF17" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
Before Width: | Height: | Size: 975 B |
BIN
public/og.jpg
Before Width: | Height: | Size: 112 KiB |
12
src/app.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
19
src/app.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="%sveltekit.assets%/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="%sveltekit.assets%/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="%sveltekit.assets%/favicon-16x16.png">
|
||||
<link rel="manifest" href="%sveltekit.assets%/site.webmanifest">
|
||||
<link rel="mask-icon" href="%sveltekit.assets%/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents" class="h-full overflow-hidden">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
4
src/app.postcss
Normal file
|
@ -0,0 +1,4 @@
|
|||
html,
|
||||
body {
|
||||
@apply h-full overflow-hidden
|
||||
}
|
BIN
src/lib/assets/triple-logo.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
src/lib/assets/vectors/github.glb
Normal file
1
src/lib/assets/vectors/github.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>
|
After Width: | Height: | Size: 963 B |
BIN
src/lib/assets/videowall-irl.jpeg
Normal file
After Width: | Height: | Size: 696 KiB |
BIN
src/lib/assets/zaantje-3d.png
Normal file
After Width: | Height: | Size: 272 KiB |
BIN
src/lib/assets/zaantje-logo.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
13
src/lib/calculate-age.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
export function calculateAge(birthdate: string): number {
|
||||
const birthDate = new Date(birthdate);
|
||||
const currentDate = new Date();
|
||||
|
||||
let age = currentDate.getFullYear() - birthDate.getFullYear();
|
||||
const monthDiff = currentDate.getMonth() - birthDate.getMonth();
|
||||
|
||||
if (monthDiff < 0 || (monthDiff === 0 && currentDate.getDate() < birthDate.getDate())) {
|
||||
age--;
|
||||
}
|
||||
|
||||
return age;
|
||||
}
|
30
src/lib/components/Footer.svelte
Normal file
|
@ -0,0 +1,30 @@
|
|||
<script>
|
||||
import GitHub from './icons/GitHub.svelte';
|
||||
import Svelte from './icons/Svelte.svelte';
|
||||
import Vercel from './icons/Vercel.svelte';
|
||||
</script>
|
||||
|
||||
<footer class="text-center px-4 py-2">
|
||||
<p>
|
||||
<a href="https://svelte.dev/" target="_blank" rel="noopener noreferrer">
|
||||
Made with <Svelte />
|
||||
</a>
|
||||
|
|
||||
<a href="https://vercel.com/" target="_blank" rel="noopener noreferrer">
|
||||
Hosted on <Vercel />
|
||||
</a>
|
||||
|
|
||||
<a href="https://github.com/bartvdbraak/hellob.art" target="_blank" rel="noopener noreferrer">
|
||||
Source code at <GitHub />
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
Licensed under GPLv3 — © {new Date().getFullYear()} hellob.art — Bart van der Braak
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
:global(.inline-svg) {
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
34
src/lib/components/Header.svelte
Normal file
|
@ -0,0 +1,34 @@
|
|||
<script lang="ts">
|
||||
import { AppBar, LightSwitch, ProgressBar, drawerStore } from '@skeletonlabs/skeleton';
|
||||
import GitHub from './icons/GitHub.svelte';
|
||||
import Hamburger from './icons/Hamburger.svelte';
|
||||
|
||||
export let progress: number;
|
||||
|
||||
function drawerOpen(): void {
|
||||
drawerStore.open();
|
||||
}
|
||||
</script>
|
||||
|
||||
<AppBar>
|
||||
<svelte:fragment slot="lead">
|
||||
<button class="md:hidden btn btn-sm mr-4" on:click={drawerOpen}>
|
||||
<span>
|
||||
<Hamburger />
|
||||
</span>
|
||||
</button>
|
||||
<img src="./icon.svg" alt="Logo" srcset="" class="pr-2" />
|
||||
<code class="code">hellob.art</code>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="trail">
|
||||
<a
|
||||
href="https://github.com/bartvdbraak/hellob.art"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn-icon variant-primary"><GitHub /></a
|
||||
>
|
||||
<LightSwitch />
|
||||
</svelte:fragment>
|
||||
</AppBar>
|
||||
|
||||
<ProgressBar label="Progress Bar" value={progress} max={100} rounded="" />
|
21
src/lib/components/Navigation.svelte
Normal file
|
@ -0,0 +1,21 @@
|
|||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { drawerStore } from '@skeletonlabs/skeleton';
|
||||
$: classesActive = (href: string) => (href === $page.url.pathname ? '!bg-primary-500' : '');
|
||||
|
||||
export let routes: { url: string; label: string }[];
|
||||
|
||||
function drawerClose(): void {
|
||||
drawerStore.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<nav class="list-nav p-4">
|
||||
<ul>
|
||||
{#each routes as route}
|
||||
<li>
|
||||
<a class="{classesActive(route.url)}" href={route.url} on:click={drawerClose}>{route.label}</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</nav>
|
41
src/lib/components/ProjectCard.svelte
Normal file
|
@ -0,0 +1,41 @@
|
|||
<script>
|
||||
import { Avatar } from '@skeletonlabs/skeleton';
|
||||
|
||||
export let link = '';
|
||||
export let headerImage = '';
|
||||
export let headerSubTitle = '';
|
||||
export let title = '';
|
||||
export let description = '';
|
||||
export let logo = '';
|
||||
/**
|
||||
* @type {any[]}
|
||||
*/
|
||||
export let contributors = [];
|
||||
export let date = '';
|
||||
</script>
|
||||
|
||||
<a class="card bg-initial card-hover overflow-hidden" href={link}>
|
||||
<header>
|
||||
<img src={headerImage} class="bg-black/50 w-full aspect-[21/9] object-cover" alt="Post" />
|
||||
</header>
|
||||
<div class="p-4 space-y-4">
|
||||
<h6 class="h6">{headerSubTitle}</h6>
|
||||
<h3 class="h3" data-toc-ignore>{title}</h3>
|
||||
<article>
|
||||
<p>
|
||||
{description}
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
<hr class="opacity-50" />
|
||||
<footer class="p-4 flex justify-start items-center space-x-4">
|
||||
<Avatar src={logo} width="w-8" />
|
||||
<div class="flex-auto flex justify-between items-center">
|
||||
{#each contributors as contributor}
|
||||
<Avatar src={contributor.imageSrc} width="w-8" />
|
||||
{/each}
|
||||
|
||||
<small>{new Date(date).toLocaleDateString()}</small>
|
||||
</div>
|
||||
</footer>
|
||||
</a>
|
105
src/lib/components/Tools.svelte
Normal file
|
@ -0,0 +1,105 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import * as THREE from 'three';
|
||||
|
||||
let camera: THREE.PerspectiveCamera,
|
||||
scene: THREE.Scene,
|
||||
renderer: THREE.WebGLRenderer,
|
||||
cube: THREE.Mesh<THREE.BoxGeometry, THREE.MeshBasicMaterial>;
|
||||
|
||||
const renderContainerId = 'render';
|
||||
|
||||
const toolsData = [
|
||||
{ name: 'Git', info: 'Short note about Git and its usage.' },
|
||||
{ name: 'DBeaver', info: 'Short note about DBeaver and its usage.' },
|
||||
{ name: 'Notion', info: 'Short note about Notion and its usage.' },
|
||||
{ name: 'Insomnia', info: 'Short note about Insomnia and its usage.' },
|
||||
{ name: 'Cyberduck', info: 'Short note about Cyberduck and its usage.' },
|
||||
{ name: 'Mullvad VPN', info: 'Short note about Mullvad VPN and its usage.' },
|
||||
{ name: 'Maccy', info: 'Short note about Maccy and its usage.' }
|
||||
];
|
||||
|
||||
onMount(() => {
|
||||
init();
|
||||
addTools();
|
||||
animate();
|
||||
});
|
||||
|
||||
const init = () => {
|
||||
scene = new THREE.Scene();
|
||||
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
const renderContainer = document.getElementById(renderContainerId);
|
||||
if (renderContainer) {
|
||||
renderContainer.appendChild(renderer.domElement);
|
||||
}
|
||||
|
||||
camera.position.z = 5;
|
||||
};
|
||||
|
||||
const addTool = (name: string, x: number, y: number, z: number) => {
|
||||
const geometry = new THREE.BoxGeometry();
|
||||
const material = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff });
|
||||
cube = new THREE.Mesh(geometry, material);
|
||||
cube.position.set(x, y, z);
|
||||
scene.add(cube);
|
||||
|
||||
cube.userData.name = name;
|
||||
cube.userData.info = toolsData.find((tool) => tool.name === name)?.info || '';
|
||||
|
||||
cube.addEventListener('click', () => {
|
||||
alert(`${cube.userData.name}: ${cube.userData.info}`);
|
||||
});
|
||||
};
|
||||
|
||||
const addTools = () => {
|
||||
addTool('Git', -2, 0, 0);
|
||||
addTool('DBeaver', 0, 0, 0);
|
||||
addTool('Notion', 2, 0, 0);
|
||||
addTool('Insomnia', -2, 2, 0);
|
||||
addTool('Cyberduck', 0, 2, 0);
|
||||
addTool('Mullvad VPN', 2, 2, 0);
|
||||
addTool('Maccy', 0, 4, 0);
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
renderer.clear();
|
||||
renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
const isMeshType = (object?: THREE.Object3D): object is THREE.Mesh => {
|
||||
return object?.type === 'Mesh';
|
||||
};
|
||||
|
||||
const animate = () => {
|
||||
if (!browser) {
|
||||
return;
|
||||
}
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
scene.traverse((object: THREE.Object3D) => {
|
||||
if (isMeshType(object) && object.isMesh) {
|
||||
object.rotation.x += 0.005;
|
||||
object.rotation.y += 0.005;
|
||||
}
|
||||
});
|
||||
|
||||
render();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#render {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<section id="render" />
|
29
src/lib/components/gltf/Github3d.svelte
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!--
|
||||
Auto-generated by: https://github.com/threlte/threlte/tree/main/packages/gltf
|
||||
Command: npx @threlte/gltf@1.0.0-next.13 ./src/lib/assets/vectors/github.glb --transform
|
||||
-->
|
||||
|
||||
<script>
|
||||
import { Group } from 'three';
|
||||
import { T, forwardEventHandlers } from '@threlte/core';
|
||||
import { useGltf } from '@threlte/extras';
|
||||
export const ref = new Group();
|
||||
|
||||
const gltf = useGltf('./github-transformed.glb', { useDraco: true });
|
||||
|
||||
const component = forwardEventHandlers();
|
||||
</script>
|
||||
|
||||
<T is={ref} dispose={false} {...$$restProps} bind:this={$component}>
|
||||
{#await gltf}
|
||||
<slot name="fallback" />
|
||||
{:then gltf}
|
||||
<T.Mesh geometry={gltf.nodes.Github_Mesh.geometry}
|
||||
><T.MeshPhysicalMaterial color={[0,0,0]} /></T.Mesh
|
||||
>
|
||||
{:catch error}
|
||||
<slot name="error" {error} />
|
||||
{/await}
|
||||
|
||||
<slot {ref} />
|
||||
</T>
|
1
src/lib/components/icons/GitHub.svelte
Normal file
|
@ -0,0 +1 @@
|
|||
<svg class="inline-svg" stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg>
|
After Width: | Height: | Size: 517 B |
1
src/lib/components/icons/Hamburger.svelte
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-menu"><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/></svg>
|
After Width: | Height: | Size: 326 B |
1
src/lib/components/icons/Svelte.svelte
Normal file
|
@ -0,0 +1 @@
|
|||
<svg class="inline-svg" stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><title></title><path d="M10.354 21.125a4.44 4.44 0 0 1-4.765-1.767 4.109 4.109 0 0 1-.703-3.107 3.898 3.898 0 0 1 .134-.522l.105-.321.287.21a7.21 7.21 0 0 0 2.186 1.092l.208.063-.02.208a1.253 1.253 0 0 0 .226.83 1.337 1.337 0 0 0 1.435.533 1.231 1.231 0 0 0 .343-.15l5.59-3.562a1.164 1.164 0 0 0 .524-.778 1.242 1.242 0 0 0-.211-.937 1.338 1.338 0 0 0-1.435-.533 1.23 1.23 0 0 0-.343.15l-2.133 1.36a4.078 4.078 0 0 1-1.135.499 4.44 4.44 0 0 1-4.765-1.766 4.108 4.108 0 0 1-.702-3.108 3.855 3.855 0 0 1 1.742-2.582l5.589-3.563a4.072 4.072 0 0 1 1.135-.499 4.44 4.44 0 0 1 4.765 1.767 4.109 4.109 0 0 1 .703 3.107 3.943 3.943 0 0 1-.134.522l-.105.321-.286-.21a7.204 7.204 0 0 0-2.187-1.093l-.208-.063.02-.207a1.255 1.255 0 0 0-.226-.831 1.337 1.337 0 0 0-1.435-.532 1.231 1.231 0 0 0-.343.15L8.62 9.368a1.162 1.162 0 0 0-.524.778 1.24 1.24 0 0 0 .211.937 1.338 1.338 0 0 0 1.435.533 1.235 1.235 0 0 0 .344-.151l2.132-1.36a4.067 4.067 0 0 1 1.135-.498 4.44 4.44 0 0 1 4.765 1.766 4.108 4.108 0 0 1 .702 3.108 3.857 3.857 0 0 1-1.742 2.583l-5.589 3.562a4.072 4.072 0 0 1-1.135.499m10.358-17.95C18.484-.015 14.082-.96 10.9 1.068L5.31 4.63a6.412 6.412 0 0 0-2.896 4.295 6.753 6.753 0 0 0 .666 4.336 6.43 6.43 0 0 0-.96 2.396 6.833 6.833 0 0 0 1.168 5.167c2.229 3.19 6.63 4.135 9.812 2.108l5.59-3.562a6.41 6.41 0 0 0 2.896-4.295 6.756 6.756 0 0 0-.665-4.336 6.429 6.429 0 0 0 .958-2.396 6.831 6.831 0 0 0-1.167-5.168Z"></path></svg>
|
After Width: | Height: | Size: 1.6 KiB |
1
src/lib/components/icons/Vercel.svelte
Normal file
|
@ -0,0 +1 @@
|
|||
<svg class="inline-svg" stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M3 19h18l-9 -15z"></path></svg>
|
After Width: | Height: | Size: 299 B |
45
src/lib/extrude-svg.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import type * as THREE from 'three';
|
||||
import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader';
|
||||
import type { SVGResult, SVGResultPaths } from 'three/examples/jsm/loaders/SVGLoader';
|
||||
|
||||
import { Mesh } from 'three/src/objects/Mesh';
|
||||
import { Group } from 'three/src/objects/Group';
|
||||
import { MeshNormalMaterial } from 'three/src/materials/MeshNormalMaterial';
|
||||
import { ExtrudeGeometry } from 'three/src/geometries/ExtrudeGeometry';
|
||||
|
||||
/**
|
||||
* Parses the provided SVG markup and extrudes it into a 3D model using THREE.js.
|
||||
* @param svgMarkup - SVG markup to extrude.
|
||||
* @return Group containing all of the extruded SVG paths.
|
||||
* @throws Error If the SVG markup is empty.
|
||||
*/
|
||||
export function extrudeSvg(svgMarkup: string): Group {
|
||||
if (!svgMarkup) {
|
||||
throw new Error('SVG markup is empty');
|
||||
}
|
||||
|
||||
const svgData: SVGResult = new SVGLoader().parse(svgMarkup);
|
||||
const material: MeshNormalMaterial = new MeshNormalMaterial();
|
||||
|
||||
const svgGroup: Mesh[][] = svgData.paths.map(createShapeFromPath);
|
||||
|
||||
const group = new Group();
|
||||
svgGroup.flat().forEach(mesh => group.add(mesh));
|
||||
|
||||
return group;
|
||||
|
||||
function createShapeFromPath(path: SVGResultPaths): Mesh[] {
|
||||
const shapes: THREE.Shape[] = path.toShapes(true);
|
||||
|
||||
return shapes.map(shape => extrudeShape(shape, material));
|
||||
}
|
||||
|
||||
function extrudeShape(shape: THREE.Shape, material: MeshNormalMaterial): Mesh {
|
||||
const geometry = new ExtrudeGeometry(shape, {
|
||||
depth: 20,
|
||||
bevelEnabled: false
|
||||
});
|
||||
|
||||
return new Mesh(geometry, material);
|
||||
}
|
||||
}
|
47
src/routes/+layout.svelte
Normal file
|
@ -0,0 +1,47 @@
|
|||
<script lang="ts">
|
||||
import '../theme.postcss';
|
||||
import '@skeletonlabs/skeleton/styles/skeleton.css';
|
||||
import '../app.postcss';
|
||||
import { AppShell, Drawer, ProgressBar, drawerStore } from '@skeletonlabs/skeleton';
|
||||
import Footer from '../lib/components/Footer.svelte';
|
||||
import Navigation from '../lib/components/Navigation.svelte';
|
||||
import Header from '$lib/components/Header.svelte';
|
||||
|
||||
let routes = [
|
||||
{ url: '/', label: 'Home' },
|
||||
{ url: '/projects', label: 'Projects' },
|
||||
{ url: '/tools', label: 'Tools' },
|
||||
{ url: '/blog', label: 'Blog' }
|
||||
];
|
||||
|
||||
let progress = 0;
|
||||
|
||||
function handleScroll(event: Event) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = event.currentTarget as HTMLElement;
|
||||
progress = (scrollTop / (scrollHeight - clientHeight)) * 100;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Drawer width="w-[280px] md:w-[480px]">
|
||||
<Navigation {routes} />
|
||||
</Drawer>
|
||||
|
||||
<AppShell
|
||||
slotSidebarLeft="w-0 md:w-52"
|
||||
on:scroll={handleScroll}
|
||||
>
|
||||
<svelte:fragment slot="header">
|
||||
<Header {progress} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="sidebarLeft">
|
||||
<Navigation {routes} />
|
||||
</svelte:fragment>
|
||||
<!-- Router Slot -->
|
||||
<div class="container p-10 mx-auto">
|
||||
<slot />
|
||||
</div>
|
||||
<!-- ---- / ---- -->
|
||||
<svelte:fragment slot="pageFooter">
|
||||
<Footer />
|
||||
</svelte:fragment>
|
||||
</AppShell>
|
63
src/routes/+page.svelte
Normal file
|
@ -0,0 +1,63 @@
|
|||
<script lang='ts'>
|
||||
import { calculateAge } from '$lib/calculate-age';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>hellob.art — home</title>
|
||||
</svelte:head>
|
||||
|
||||
<main class="container mx-auto px-4 py-8 text-left">
|
||||
<h2 class="text-3xl font-bold mb-4">About Me</h2>
|
||||
|
||||
<p class="text-lg leading-relaxed mb-8">
|
||||
Welcome to my homepage! I'm a passionate DevOps engineer based in Zaandam, Netherlands. At {calculateAge("1994-10-18")}
|
||||
years old, I've already gathered a wealth of experience and expertise in the world of
|
||||
technology. From programming in Python, Javascript, and Bash to utilizing cutting-edge
|
||||
technologies like Terraform, Bicep, and Kubernetes, I've honed my skills to drive efficient and
|
||||
innovative solutions. My journey in the tech industry began during my Information Sciences
|
||||
studies, where I fell in love with data-driven projects and statistical learning. This passion
|
||||
led me to explore languages like Python and LaTeX, enabling me to bring ideas to life through
|
||||
powerful data management and visualization.
|
||||
</p>
|
||||
|
||||
<h2 class="text-3xl font-bold mb-4">Empowering the Cloud with Azure</h2>
|
||||
|
||||
<p class="text-lg leading-relaxed mb-8">
|
||||
My career took off when I joined Triple as a DevOps Engineer. At Triple, I've embraced the world
|
||||
of Azure and Azure DevOps, mastering the intricacies of cloud architecture and deployment.
|
||||
Holding certifications like AZ-104 and CKA has further solidified my expertise, empowering me to
|
||||
build and manage robust, scalable, and secure cloud environments. My proficiency in
|
||||
containerization and Kubernetes has allowed me to take application development to new heights,
|
||||
ensuring seamless and efficient deployment at scale.
|
||||
</p>
|
||||
|
||||
<h2 class="text-3xl font-bold mb-4">Solving Problems with Code and Automation</h2>
|
||||
|
||||
<p class="text-lg leading-relaxed mb-8">
|
||||
As a DevOps engineer, I thrive on solving complex challenges with the power of code and
|
||||
automation. My passion for streamlining workflows led me to create internal tooling using APIs,
|
||||
boosting productivity for myself and my colleagues. Outside of work, I enjoy taking on side
|
||||
projects that push my boundaries, expanding my skill set, and exploring new technologies. I
|
||||
strongly believe that innovation and continuous learning are key drivers of success in the
|
||||
ever-evolving tech landscape.
|
||||
</p>
|
||||
|
||||
<h2 class="text-3xl font-bold mb-4">Cat Lover and Whiskey Enthusiast</h2>
|
||||
|
||||
<p class="text-lg leading-relaxed mb-8">
|
||||
When I'm not busy crafting technological solutions, you'll often find me indulging in two of my
|
||||
favorite passions: cats and whiskey. There's something special about the companionship of cats,
|
||||
and their playful antics never fail to bring joy to my day. As for whiskey, I appreciate the
|
||||
intricate flavors and the artistry behind its production. I find both interests to be a
|
||||
delightful way to unwind and recharge my creative energies.
|
||||
</p>
|
||||
|
||||
<h2 class="text-3xl font-bold mb-4">Let's Connect</h2>
|
||||
|
||||
<p class="text-lg leading-relaxed">
|
||||
I'm always eager to collaborate on exciting projects and explore new opportunities. Whether it's
|
||||
discussing data-driven ideas, cloud architecture, or just sharing cute cat pictures, I'd love to
|
||||
connect with like-minded individuals. Feel free to reach out to me through the provided contact
|
||||
information. Let's work together to create something extraordinary!
|
||||
</p>
|
||||
</main>
|
7
src/routes/blog/+page.svelte
Normal file
|
@ -0,0 +1,7 @@
|
|||
<svelte:head>
|
||||
<title>hellob.art — blog</title>
|
||||
</svelte:head>
|
||||
|
||||
<main class="container mx-auto px-4 py-8 text-left">
|
||||
<h2 class="text-3xl font-bold mb-8">Blog posts</h2>
|
||||
</main>
|
0
src/routes/blog/[slug]/$types.d.ts
vendored
Normal file
11
src/routes/blog/[slug]/+page.svelte
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script>
|
||||
/** @type {import('./$types').PageData} */
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>hellob.art — {data.title}</title>
|
||||
</svelte:head>
|
||||
|
||||
<!-- <h1>{data.title}</h1>
|
||||
<div>{@html data.content}</div> -->
|
56
src/routes/projects/+page.svelte
Normal file
|
@ -0,0 +1,56 @@
|
|||
<script>
|
||||
import videowallImage from '$lib/assets/videowall-irl.jpeg';
|
||||
import tripleLogo from '$lib/assets/triple-logo.png';
|
||||
import zaantjeImage from '$lib/assets/zaantje-3d.png';
|
||||
import zaantjeLogo from '$lib/assets/zaantje-logo.png';
|
||||
import ProjectCard from '$lib/components/ProjectCard.svelte';
|
||||
|
||||
let projects = [
|
||||
{
|
||||
id: 1,
|
||||
link: '#',
|
||||
headerImage: videowallImage,
|
||||
headerSubTitle: 'Internal Project',
|
||||
title: 'Videowall',
|
||||
description: `An internal application to control an impressive 6x5 monitor setup with a user-friendly
|
||||
frontend built on Next.js and a powerful backend developed in Golang.`,
|
||||
logo: tripleLogo,
|
||||
contributors: [],
|
||||
date: '2021'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
link: 'https://zaantje.com',
|
||||
headerImage: zaantjeImage,
|
||||
headerSubTitle: 'Personal Project',
|
||||
title: 'Zaantje',
|
||||
description: `A SPA crafted with Nuxt.js and Vue.js, backed by Sanity CMS, taking you
|
||||
on a virtual tour of Zaandam, showcasing locations of famous music videos.`,
|
||||
logo: zaantjeLogo,
|
||||
contributors: [],
|
||||
date: '2020'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>hellob.art — projects</title>
|
||||
</svelte:head>
|
||||
|
||||
<main class="container mx-auto px-4 py-8 text-left">
|
||||
<h2 class="text-3xl font-bold mb-8">My Projects</h2>
|
||||
<div class="w-full text-token grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{#each projects as project}
|
||||
<ProjectCard
|
||||
link={project.link}
|
||||
headerImage={project.headerImage}
|
||||
headerSubTitle={project.headerSubTitle}
|
||||
title={project.title}
|
||||
description={project.description}
|
||||
logo={project.logo}
|
||||
contributors={project.contributors}
|
||||
date={project.date}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</main>
|
18
src/routes/tools/+page.svelte
Normal file
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { Canvas } from '@threlte/core';
|
||||
import Scene from './Scene.svelte';
|
||||
import Github3d from '$lib/components/gltf/Github3d.svelte';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>hellob.art — tools</title>
|
||||
</svelte:head>
|
||||
|
||||
<main class="container mx-auto px-4 py-8 text-left">
|
||||
<h2 class="text-3xl font-bold mb-8">Tools</h2>
|
||||
|
||||
<Canvas>
|
||||
<Scene />
|
||||
<Github3d />
|
||||
</Canvas>
|
||||
</main>
|
42
src/routes/tools/Scene.svelte
Normal file
|
@ -0,0 +1,42 @@
|
|||
<script>
|
||||
import Github3d from '$lib/components/gltf/Github3d.svelte';
|
||||
import { T, useFrame } from '@threlte/core';
|
||||
import { interactivity } from '@threlte/extras';
|
||||
import { spring } from 'svelte/motion';
|
||||
|
||||
interactivity();
|
||||
const scale = spring(1);
|
||||
|
||||
let rotation = 0;
|
||||
useFrame((_state, delta) => {
|
||||
rotation += delta;
|
||||
});
|
||||
</script>
|
||||
|
||||
<T.PerspectiveCamera
|
||||
makeDefault
|
||||
position={[10, 10, 10]}
|
||||
on:create={({ ref }) => {
|
||||
ref.lookAt(0, 1, 0);
|
||||
}}
|
||||
/>
|
||||
|
||||
<T.Mesh
|
||||
rotation.y={rotation}
|
||||
position.y={1}
|
||||
scale={$scale}
|
||||
on:pointerenter={() => scale.set(1.5)}
|
||||
on:pointerleave={() => scale.set(1)}
|
||||
on:click={() => scale.set(3)}
|
||||
>
|
||||
<T.BoxGeometry args={[1, 2, 1]} />
|
||||
<T.MeshBasicMaterial color={[0, 0, 0]} />
|
||||
</T.Mesh>
|
||||
|
||||
<Github3d
|
||||
position={[0, 0, 0]}
|
||||
scale={$scale}
|
||||
on:pointerenter={() => scale.set(1.5)}
|
||||
on:pointerleave={() => scale.set(1)}
|
||||
on:click={() => scale.set(3)}
|
||||
/>
|
98
src/theme.postcss
Normal file
|
@ -0,0 +1,98 @@
|
|||
|
||||
:root {
|
||||
/* =~= Theme Properties =~= */
|
||||
--theme-font-family-base: system-ui;
|
||||
--theme-font-family-heading: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--theme-font-color-base: 0 0 0;
|
||||
--theme-font-color-dark: 255 255 255;
|
||||
--theme-rounded-base: 4px;
|
||||
--theme-rounded-container: 12px;
|
||||
--theme-border-base: 2px;
|
||||
/* =~= Theme On-X Colors =~= */
|
||||
--on-primary: 0 0 0;
|
||||
--on-secondary: 255 255 255;
|
||||
--on-tertiary: 0 0 0;
|
||||
--on-success: 0 0 0;
|
||||
--on-warning: 0 0 0;
|
||||
--on-error: 255 255 255;
|
||||
--on-surface: 255 255 255;
|
||||
/* =~= Theme Colors =~= */
|
||||
/* primary | #67a1ba */
|
||||
--color-primary-50: 232 241 245; /* ⬅ #e8f1f5 */
|
||||
--color-primary-100: 225 236 241; /* ⬅ #e1ecf1 */
|
||||
--color-primary-200: 217 232 238; /* ⬅ #d9e8ee */
|
||||
--color-primary-300: 194 217 227; /* ⬅ #c2d9e3 */
|
||||
--color-primary-400: 149 189 207; /* ⬅ #95bdcf */
|
||||
--color-primary-500: 103 161 186; /* ⬅ #67a1ba */
|
||||
--color-primary-600: 93 145 167; /* ⬅ #5d91a7 */
|
||||
--color-primary-700: 77 121 140; /* ⬅ #4d798c */
|
||||
--color-primary-800: 62 97 112; /* ⬅ #3e6170 */
|
||||
--color-primary-900: 50 79 91; /* ⬅ #324f5b */
|
||||
/* secondary | #4F46E5 */
|
||||
--color-secondary-50: 229 227 251; /* ⬅ #e5e3fb */
|
||||
--color-secondary-100: 220 218 250; /* ⬅ #dcdafa */
|
||||
--color-secondary-200: 211 209 249; /* ⬅ #d3d1f9 */
|
||||
--color-secondary-300: 185 181 245; /* ⬅ #b9b5f5 */
|
||||
--color-secondary-400: 132 126 237; /* ⬅ #847eed */
|
||||
--color-secondary-500: 79 70 229; /* ⬅ #4F46E5 */
|
||||
--color-secondary-600: 71 63 206; /* ⬅ #473fce */
|
||||
--color-secondary-700: 59 53 172; /* ⬅ #3b35ac */
|
||||
--color-secondary-800: 47 42 137; /* ⬅ #2f2a89 */
|
||||
--color-secondary-900: 39 34 112; /* ⬅ #272270 */
|
||||
/* tertiary | #0EA5E9 */
|
||||
--color-tertiary-50: 219 242 252; /* ⬅ #dbf2fc */
|
||||
--color-tertiary-100: 207 237 251; /* ⬅ #cfedfb */
|
||||
--color-tertiary-200: 195 233 250; /* ⬅ #c3e9fa */
|
||||
--color-tertiary-300: 159 219 246; /* ⬅ #9fdbf6 */
|
||||
--color-tertiary-400: 86 192 240; /* ⬅ #56c0f0 */
|
||||
--color-tertiary-500: 14 165 233; /* ⬅ #0EA5E9 */
|
||||
--color-tertiary-600: 13 149 210; /* ⬅ #0d95d2 */
|
||||
--color-tertiary-700: 11 124 175; /* ⬅ #0b7caf */
|
||||
--color-tertiary-800: 8 99 140; /* ⬅ #08638c */
|
||||
--color-tertiary-900: 7 81 114; /* ⬅ #075172 */
|
||||
/* success | #84cc16 */
|
||||
--color-success-50: 237 247 220; /* ⬅ #edf7dc */
|
||||
--color-success-100: 230 245 208; /* ⬅ #e6f5d0 */
|
||||
--color-success-200: 224 242 197; /* ⬅ #e0f2c5 */
|
||||
--color-success-300: 206 235 162; /* ⬅ #ceeba2 */
|
||||
--color-success-400: 169 219 92; /* ⬅ #a9db5c */
|
||||
--color-success-500: 132 204 22; /* ⬅ #84cc16 */
|
||||
--color-success-600: 119 184 20; /* ⬅ #77b814 */
|
||||
--color-success-700: 99 153 17; /* ⬅ #639911 */
|
||||
--color-success-800: 79 122 13; /* ⬅ #4f7a0d */
|
||||
--color-success-900: 65 100 11; /* ⬅ #41640b */
|
||||
/* warning | #EAB308 */
|
||||
--color-warning-50: 252 244 218; /* ⬅ #fcf4da */
|
||||
--color-warning-100: 251 240 206; /* ⬅ #fbf0ce */
|
||||
--color-warning-200: 250 236 193; /* ⬅ #faecc1 */
|
||||
--color-warning-300: 247 225 156; /* ⬅ #f7e19c */
|
||||
--color-warning-400: 240 202 82; /* ⬅ #f0ca52 */
|
||||
--color-warning-500: 234 179 8; /* ⬅ #EAB308 */
|
||||
--color-warning-600: 211 161 7; /* ⬅ #d3a107 */
|
||||
--color-warning-700: 176 134 6; /* ⬅ #b08606 */
|
||||
--color-warning-800: 140 107 5; /* ⬅ #8c6b05 */
|
||||
--color-warning-900: 115 88 4; /* ⬅ #735804 */
|
||||
/* error | #d31922 */
|
||||
--color-error-50: 248 221 222; /* ⬅ #f8ddde */
|
||||
--color-error-100: 246 209 211; /* ⬅ #f6d1d3 */
|
||||
--color-error-200: 244 198 200; /* ⬅ #f4c6c8 */
|
||||
--color-error-300: 237 163 167; /* ⬅ #eda3a7 */
|
||||
--color-error-400: 224 94 100; /* ⬅ #e05e64 */
|
||||
--color-error-500: 211 25 34; /* ⬅ #d31922 */
|
||||
--color-error-600: 190 23 31; /* ⬅ #be171f */
|
||||
--color-error-700: 158 19 26; /* ⬅ #9e131a */
|
||||
--color-error-800: 127 15 20; /* ⬅ #7f0f14 */
|
||||
--color-error-900: 103 12 17; /* ⬅ #670c11 */
|
||||
/* surface | #063142 */
|
||||
--color-surface-50: 218 224 227; /* ⬅ #dae0e3 */
|
||||
--color-surface-100: 205 214 217; /* ⬅ #cdd6d9 */
|
||||
--color-surface-200: 193 204 208; /* ⬅ #c1ccd0 */
|
||||
--color-surface-300: 155 173 179; /* ⬅ #9badb3 */
|
||||
--color-surface-400: 81 111 123; /* ⬅ #516f7b */
|
||||
--color-surface-500: 6 49 66; /* ⬅ #063142 */
|
||||
--color-surface-600: 5 44 59; /* ⬅ #052c3b */
|
||||
--color-surface-700: 5 37 50; /* ⬅ #052532 */
|
||||
--color-surface-800: 4 29 40; /* ⬅ #041d28 */
|
||||
--color-surface-900: 3 24 32; /* ⬅ #031820 */
|
||||
|
||||
}
|
BIN
static/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
static/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 14 KiB |
9
static/browserconfig.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
BIN
static/favicon-16x16.png
Normal file
After Width: | Height: | Size: 910 B |
BIN
static/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
static/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/github-transformed.glb
Normal file
9
static/icon.svg
Normal file
|
@ -0,0 +1,9 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<radialGradient id="myGradient" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
|
||||
<stop offset="0%" stop-color="#314755" />
|
||||
<stop offset="100%" stop-color="#26a0da" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm0 13a3 3 0 1 1 3-3 3 3 0 0 1-3 3zm2.75-7.17A5 5 0 0 0 13 7.1v-3a7.94 7.94 0 0 1 3.9 1.62zM11 7.1a5 5 0 0 0-1.75.73L7.1 5.69A7.94 7.94 0 0 1 11 4.07zM7.83 9.25A5 5 0 0 0 7.1 11h-3a7.94 7.94 0 0 1 1.59-3.9zM7.1 13a5 5 0 0 0 .73 1.75L5.69 16.9A7.94 7.94 0 0 1 4.07 13zm2.15 3.17a5 5 0 0 0 1.75.73v3a7.94 7.94 0 0 1-3.9-1.62zm3.75.73a5 5 0 0 0 1.75-.73l2.15 2.14a7.94 7.94 0 0 1-3.9 1.62zm3.17-2.15A5 5 0 0 0 16.9 13h3a7.94 7.94 0 0 1-1.62 3.9zM16.9 11a5 5 0 0 0-.73-1.75l2.14-2.15a7.94 7.94 0 0 1 1.62 3.9z" fill="url(#myGradient)"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 901 B |
BIN
static/mstile-150x150.png
Normal file
After Width: | Height: | Size: 10 KiB |
47
static/safari-pinned-tab.svg
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M3378 6415 c-2 -2 -46 -5 -98 -9 -52 -3 -117 -8 -145 -12 -49 -6
|
||||
-271 -47 -310 -57 -11 -3 -33 -8 -50 -12 -217 -51 -536 -183 -760 -316 -336
|
||||
-198 -660 -494 -881 -804 -117 -164 -239 -379 -304 -535 -17 -41 -37 -88 -45
|
||||
-105 -40 -88 -121 -364 -144 -490 -18 -96 -20 -107 -30 -175 -33 -208 -33
|
||||
-561 -1 -795 88 -626 371 -1202 810 -1645 142 -143 172 -170 290 -261 349
|
||||
-272 768 -465 1196 -553 65 -14 142 -28 169 -31 28 -4 61 -8 75 -11 134 -23
|
||||
592 -23 705 1 11 2 39 7 63 9 173 21 518 112 647 171 17 7 54 23 84 35 145 57
|
||||
438 224 586 335 108 80 232 190 345 305 659 668 949 1595 794 2535 -158 957
|
||||
-824 1801 -1723 2184 -290 124 -569 195 -891 226 -63 7 -377 14 -382 10z
|
||||
m-168 -1045 l0 -439 -42 -11 c-146 -37 -298 -99 -411 -167 -32 -19 -60 -34
|
||||
-62 -32 -2 2 -144 143 -314 313 l-311 308 78 56 c245 176 539 307 847 379 42
|
||||
9 171 31 193 32 l22 1 0 -440z m710 414 c311 -48 715 -226 987 -435 23 -17 22
|
||||
-17 -256 -290 -154 -151 -295 -289 -314 -308 l-35 -33 -60 36 c-107 65 -271
|
||||
131 -409 166 l-43 11 0 436 0 436 48 -7 c26 -4 63 -9 82 -12z m-1931 -1187
|
||||
c157 -159 288 -290 290 -292 2 -2 -13 -31 -33 -64 -64 -104 -143 -297 -161
|
||||
-389 -13 -68 31 -62 -452 -62 l-435 0 6 54 c4 29 9 64 11 77 3 13 9 49 15 79
|
||||
9 52 21 95 57 215 28 92 84 229 140 340 54 109 79 150 177 297 l51 77 24 -22
|
||||
c13 -12 152 -152 310 -310z m3476 153 c122 -188 227 -424 284 -641 29 -109 60
|
||||
-264 61 -301 0 -17 -28 -18 -440 -18 l-439 0 -11 43 c-34 137 -101 302 -166
|
||||
409 -20 32 -35 61 -33 63 2 2 143 144 313 314 l308 311 31 -43 c17 -23 58 -85
|
||||
92 -137z m-1845 -384 c172 -21 347 -106 484 -235 359 -340 358 -920 -1 -1262
|
||||
-350 -334 -903 -322 -1234 27 -159 169 -242 377 -243 609 0 356 231 687 569
|
||||
814 33 13 67 24 75 26 157 32 224 36 350 21z m-1540 -1199 c40 -153 98 -297
|
||||
167 -410 19 -32 34 -60 32 -62 -2 -2 -142 -143 -311 -313 l-308 -310 -29 36
|
||||
c-53 68 -158 237 -206 331 -118 237 -202 499 -232 728 l-5 42 441 0 440 0 11
|
||||
-42z m3720 19 c0 -39 -39 -224 -70 -333 -71 -247 -193 -497 -346 -710 l-51
|
||||
-72 -269 273 c-148 151 -286 292 -308 314 l-38 40 35 59 c67 111 128 260 167
|
||||
410 l11 42 434 0 435 0 0 -23z m-3041 -940 c111 -67 264 -129 409 -166 l42
|
||||
-11 0 -434 c0 -493 11 -443 -91 -425 -30 5 -69 12 -88 15 -153 26 -448 130
|
||||
-598 212 -117 63 -143 79 -208 121 -38 25 -88 61 -111 79 l-41 33 116 114
|
||||
c238 235 506 496 510 496 2 0 29 -15 60 -34z m1861 -280 l310 -309 -62 -44
|
||||
c-69 -48 -235 -153 -243 -153 -3 0 -22 -11 -43 -23 -53 -33 -260 -120 -371
|
||||
-156 -105 -34 -288 -77 -369 -87 l-52 -6 2 443 c3 384 5 442 18 445 51 10 75
|
||||
16 143 39 85 27 215 87 292 134 28 16 53 29 57 28 4 -1 147 -141 318 -311z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3 KiB |
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "hellob.art",
|
||||
"short_name": "hellob.art",
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
|
@ -13,7 +13,7 @@
|
|||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#5c5c5c",
|
||||
"background_color": "#5c5c5c",
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--primary: 222.2 47.4% 11.2%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
|
||||
--destructive: 0 100% 50%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: 215 20.2% 65.1%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71% 4%;
|
||||
--foreground: 213 31% 91%;
|
||||
|
||||
--muted: 223 47% 11%;
|
||||
--muted-foreground: 215.4 16.3% 56.9%;
|
||||
|
||||
--accent: 216 34% 17%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
|
||||
--popover: 224 71% 4%;
|
||||
--popover-foreground: 215 20.2% 65.1%;
|
||||
|
||||
--border: 216 34% 17%;
|
||||
--input: 216 34% 17%;
|
||||
|
||||
--card: 224 71% 4%;
|
||||
--card-foreground: 213 31% 91%;
|
||||
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 1.2%;
|
||||
|
||||
--secondary: 222.2 47.4% 11.2%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
|
||||
--destructive: 0 63% 31%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
|
||||
--ring: 216 34% 17%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
font-feature-settings: "rlig" 1, "calt" 1;
|
||||
}
|
||||
}
|
13
svelte.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
preprocess: [vitePreprocess({})],
|
||||
|
||||
kit: {
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
18
tailwind.config.cjs
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: 'class',
|
||||
content: [
|
||||
'./src/**/*.{html,js,svelte,ts}',
|
||||
require('path').join(require.resolve(
|
||||
'@skeletonlabs/skeleton'),
|
||||
'../**/*.{html,js,svelte,ts}'
|
||||
)
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
...require('@skeletonlabs/skeleton/tailwind/skeleton.cjs')()
|
||||
]
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
const { fontFamily } = require("tailwindcss/defaultTheme");
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./app/**/*.{ts,tsx}",
|
||||
"./components/**/*.{ts,tsx}",
|
||||
"./ui/**/*.{ts,tsx}",
|
||||
"./content/**/*.{md,mdx}",
|
||||
],
|
||||
darkMode: ["class"],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: "2rem",
|
||||
screens: {
|
||||
"2xl": "1400px",
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
border: "hsl(var(--border))",
|
||||
input: "hsl(var(--input))",
|
||||
ring: "hsl(var(--ring))",
|
||||
background: "hsl(var(--background))",
|
||||
foreground: "hsl(var(--foreground))",
|
||||
primary: {
|
||||
DEFAULT: "hsl(var(--primary))",
|
||||
foreground: "hsl(var(--primary-foreground))",
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: "hsl(var(--secondary))",
|
||||
foreground: "hsl(var(--secondary-foreground))",
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: "hsl(var(--destructive))",
|
||||
foreground: "hsl(var(--destructive-foreground))",
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: "hsl(var(--muted))",
|
||||
foreground: "hsl(var(--muted-foreground))",
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: "hsl(var(--accent))",
|
||||
foreground: "hsl(var(--accent-foreground))",
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: "hsl(var(--popover))",
|
||||
foreground: "hsl(var(--popover-foreground))",
|
||||
},
|
||||
card: {
|
||||
DEFAULT: "hsl(var(--card))",
|
||||
foreground: "hsl(var(--card-foreground))",
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
lg: `var(--radius)`,
|
||||
md: `calc(var(--radius) - 2px)`,
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ["var(--font-sans)", ...fontFamily.sans],
|
||||
heading: ["var(--font-heading)", ...fontFamily.sans],
|
||||
},
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: 0 },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: 0 },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
|
||||
};
|
|
@ -1,28 +1,17 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true
|
||||
}
|
||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||
}
|
||||
|
|
11
types/index.d.ts
vendored
|
@ -1,11 +0,0 @@
|
|||
export type NavItem = {
|
||||
title: string;
|
||||
href: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export type MainNavItem = NavItem;
|
||||
|
||||
export type NavigationConfig = {
|
||||
mainNav: MainNavItem[];
|
||||
};
|
9
vite.config.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
ssr: {
|
||||
noExternal: ['three']
|
||||
}
|
||||
});
|