Merge pull request #82 from bartvdbraak/feat/workflows
Add new workflows
8
.github/dependabot.yml
vendored
|
@ -1,8 +1,8 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval: 'weekly'
|
||||
reviewers:
|
||||
- "bartvdbraak"
|
||||
- 'bartvdbraak'
|
||||
|
|
7
.github/renovate.json
vendored
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["config:base"]
|
||||
}
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["config:base"],
|
||||
"reviewers": ["bartvdbraak"]
|
||||
}
|
||||
|
|
38
.github/workflows/linting.yaml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
name: Linting
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
run-checks:
|
||||
name: Run checks
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18]
|
||||
steps:
|
||||
- name: Checkout Git repository
|
||||
uses: actions/checkout@v3.5.3
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2.4.0
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- 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
|
135
.github/workflows/unlighthouse.yaml
vendored
Normal file
|
@ -0,0 +1,135 @@
|
|||
name: Unlighthouse
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
unlighthouse:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
COMMENT_ID: unlighthouse-node${{matrix.node-version}}
|
||||
PORT: 8000
|
||||
CLOUDFLARE_PROJECT: hellobart-unlighthouse
|
||||
CLOUDFLARE_BRANCH: pull-${{ github.event.pull_request.number }}
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18]
|
||||
steps:
|
||||
- name: Create initial comment
|
||||
uses: marocchino/sticky-pull-request-comment@v2.7.0
|
||||
with:
|
||||
header: ${{ env.COMMENT_ID }}
|
||||
message: |
|
||||
⚡️ Lighthouse report
|
||||
|
||||

|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.5.3
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2.2.4
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.7.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build production
|
||||
run: pnpm run build
|
||||
|
||||
- name: Start Preview and Get Preview URL
|
||||
run: |
|
||||
pnpm run preview --port ${{ env.PORT }} & echo $! > preview_pid
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm add -g @unlighthouse/cli puppeteer
|
||||
|
||||
- name: Run Unlighthouse
|
||||
run: |
|
||||
unlighthouse-ci \
|
||||
--site "http://localhost:${{ env.PORT }}" \
|
||||
--reporter jsonExpanded \
|
||||
--build-static
|
||||
|
||||
- name: Upload report to Cloudflare pages
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
command: pages deploy .unlighthouse --project-name="${{ env.CLOUDFLARE_PROJECT }}" --branch=${{ env.CLOUDFLARE_BRANCH }}
|
||||
|
||||
- name: Create result content
|
||||
id: create_result_content
|
||||
uses: actions/github-script@v6.4.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
const result = JSON.parse(fs.readFileSync('.unlighthouse/ci-result.json', 'utf8'));
|
||||
|
||||
const formatScore = score => `${Math.round(score * 100)} (${score})`;
|
||||
const getEmoji = score => score >= 0.9 ? '🟢' : score >= 0.5 ? '🟠' : '🔴';
|
||||
|
||||
const score = res => `${getEmoji(res)} ${formatScore(res)}`;
|
||||
|
||||
const reportUrl = `https://${{ env.CLOUDFLARE_BRANCH }}.${{ env.CLOUDFLARE_PROJECT }}.pages.dev`;
|
||||
|
||||
const comment = [
|
||||
`⚡️ Lighthouse report for the changes in this PR:`,
|
||||
'| Category | Score |',
|
||||
'| --- | --- |',
|
||||
`| Performance | ${score(result.summary.categories.performance.averageScore)} |`,
|
||||
`| Accessibility | ${score(result.summary.categories.accessibility.averageScore)} |`,
|
||||
`| Best practices | ${score(result.summary.categories['best-practices'].averageScore)} |`,
|
||||
`| SEO | ${score(result.summary.categories.seo.averageScore)} |`,
|
||||
`| *Overall* | ${score(result.summary.score)} |`,
|
||||
'',
|
||||
'*Lighthouse scores for individual routes:*',
|
||||
'',
|
||||
'| Path | Performance | Accessibility | Best practices | SEO | Overall |',
|
||||
'| --- | --- | --- | --- | --- | --- |',
|
||||
`${result.routes.map(route => `| ${route.path} | ${score(route.categories.performance.score)} | ${score(route.categories.accessibility.score)} | ${score(route.categories['best-practices'].score)} | ${score(route.categories.seo.score)} | ${score(route.score)} |`).join('\n')}`,
|
||||
'',
|
||||
'*Lighthouse metrics:*',
|
||||
'',
|
||||
'| Metric | Average Value |',
|
||||
'| --- | --- |',
|
||||
`${Object.entries(result.summary.metrics).map(([metric, { averageNumericValue }]) => `| ${metric} | ${averageNumericValue} |`).join('\n')}`,
|
||||
'',
|
||||
`View the full Lighthouse report [here](${reportUrl}).`,
|
||||
].join('\n');
|
||||
|
||||
core.setOutput("comment", comment);
|
||||
|
||||
- name: Update comment with result
|
||||
uses: marocchino/sticky-pull-request-comment@v2.7.0
|
||||
with:
|
||||
header: ${{ env.COMMENT_ID }}
|
||||
message: ${{ steps.create_result_content.outputs.comment }}
|
||||
|
||||
- name: Update comment on failure
|
||||
uses: marocchino/sticky-pull-request-comment@v2.7.0
|
||||
if: ${{ failure() }}
|
||||
with:
|
||||
header: ${{ env.COMMENT_ID }}
|
||||
message: |
|
||||
⚡️ Lighthouse report failed
|
||||
|
||||
See deployment for any errors
|
||||
|
||||
- name: Update comment on cancel
|
||||
uses: marocchino/sticky-pull-request-comment@v2.7.0
|
||||
if: ${{ cancelled() }}
|
||||
with:
|
||||
header: ${{ env.COMMENT_ID }}
|
||||
message: |
|
||||
⚡️ Lighthouse report cancelled
|
1
.gitignore
vendored
|
@ -8,3 +8,4 @@ node_modules
|
|||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
.unlighthouse
|
31
README.md
|
@ -1,6 +1,6 @@
|
|||
<div align="center">
|
||||
<h1 align="center">hellob.art</h1>
|
||||
<h5>a simple portfolio</h5>
|
||||
<h5>personal website built with Svelte</h5>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
|
@ -8,11 +8,6 @@
|
|||
<a href="https://github.com/bartvdbraak/hellob.art/deployments/activity_log?environment=Production"><img src="https://img.shields.io/github/deployments/bartvdbraak/hellob.art/production?label=vercel&logo=vercel" /></a>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<a href="https://hellob.art?ref=github">hellob.art</a>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
## Installation
|
||||
|
||||
To install the project and its dependencies, follow these steps:
|
||||
|
@ -52,10 +47,7 @@ pnpm run dev -- --open
|
|||
- **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).
|
||||
- **Threlte:** Declarative Three.js for Svelte.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -65,12 +57,15 @@ I'm open to contributions! If you find any bugs, have suggestions, or want to ad
|
|||
|
||||
This project is licensed under the GPLv3 License. Feel free to explore, learn, and have fun!
|
||||
|
||||
## Get in Touch
|
||||
Some dependencies may hold different licenses but are in compliance with GPLv3:
|
||||
|
||||
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! 😊
|
||||
- `MIT`: Compatible with GPLv3.
|
||||
- `Apache 2.0`: Compatible with GPLv3.
|
||||
- `BSD-3-Clause`: Compatible with GPLv3.
|
||||
- `BSD-2-Clause`: Compatible with GPLv3.
|
||||
- `ISC`: Compatible with GPLv3.
|
||||
- `Python-2.0`: Compatible with GPLv3. (Note: Python has its own license, and version 2.0 is compatible with GPLv3).
|
||||
- `CC-BY-4.0`: This is a Creative Commons license, which is not a software license. It's generally not recommended to include CC licenses in software projects due to potential compatibility issues. This might cause complications if you choose GPLv3.
|
||||
- `CC0-1.0`: Not a software license, but it is explicitly designed to waive all copyrights, making it effectively compatible with GPLv3.
|
||||
- `0BSD`: Compatible with GPLv3.
|
||||
- `(MIT OR CC0-1.0)`: MIT is compatible with GPLv3, and CC0-1.0 is effectively compatible with GPLv3.
|
||||
|
|
|
@ -3,10 +3,7 @@ const tailwindcss = require('tailwindcss');
|
|||
const autoprefixer = require('autoprefixer');
|
||||
|
||||
const config = {
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
autoprefixer
|
||||
]
|
||||
plugins: [tailwindcss(), autoprefixer]
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
|
34
src/app.html
|
@ -1,20 +1,20 @@
|
|||
<!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">
|
||||
<title></title>
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents" class="h-full overflow-hidden">%sveltekit.body%</div>
|
||||
</body>
|
||||
<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" />
|
||||
<title></title>
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents" class="h-full overflow-hidden">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
html,
|
||||
body {
|
||||
@apply h-full overflow-hidden
|
||||
}
|
||||
@apply h-full overflow-hidden;
|
||||
}
|
||||
|
||||
.dark .logo-text-gradient-dark {
|
||||
@apply from-logo-blue-start-dark to-logo-blue-stop-dark;
|
||||
}
|
||||
|
||||
.logo-text-gradient-light {
|
||||
@apply from-logo-blue-start-light to-logo-blue-stop-light;
|
||||
}
|
||||
|
||||
.logo-text-gradient {
|
||||
@apply bg-clip-text text-transparent box-decoration-clone;
|
||||
/* Direction */
|
||||
@apply bg-gradient-to-br;
|
||||
/* Color Stops */
|
||||
@apply logo-text-gradient-light logo-text-gradient-dark;
|
||||
}
|
||||
|
|
|
@ -1,13 +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();
|
||||
const birthDate = new Date(birthdate);
|
||||
const currentDate = new Date();
|
||||
|
||||
if (monthDiff < 0 || (monthDiff === 0 && currentDate.getDate() < birthDate.getDate())) {
|
||||
age--;
|
||||
}
|
||||
let age = currentDate.getFullYear() - birthDate.getFullYear();
|
||||
const monthDiff = currentDate.getMonth() - birthDate.getMonth();
|
||||
|
||||
return age;
|
||||
if (monthDiff < 0 || (monthDiff === 0 && currentDate.getDate() < birthDate.getDate())) {
|
||||
age--;
|
||||
}
|
||||
|
||||
return age;
|
||||
}
|
||||
|
|
|
@ -13,29 +13,47 @@
|
|||
|
||||
<AppBar>
|
||||
<svelte:fragment slot="lead">
|
||||
<button aria-label="Toggle navigation menu" class="md:hidden btn btn-sm mr-4" on:click={drawerOpen}>
|
||||
<button
|
||||
aria-label="Toggle navigation menu"
|
||||
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" />
|
||||
<h2 class="code">hellob.art</h2>
|
||||
<img width="32" height="24" src="./icon.svg" alt="Logo" srcset="" class="" />
|
||||
<h1 class="h6">
|
||||
<span
|
||||
class="bg-gradient-to-br logo-text-gradient bg-clip-text text-transparent box-decoration-clone font-mono font-bold tracking-tighter pl-3"
|
||||
>hellob.art</span
|
||||
>
|
||||
</h1>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="trail">
|
||||
<a
|
||||
href="https://linkedin.com/in/bartvdbraak"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn-icon btn-icon-sm hover:variant-soft-primary"><LinkedIn /><span class="sr-only">LinkedIn Profile of Bart van der Braak</span></a
|
||||
class="btn-icon btn-icon-sm hover:variant-soft-primary"
|
||||
><LinkedIn /><span class="sr-only">LinkedIn Profile of Bart van der Braak</span></a
|
||||
>
|
||||
<a
|
||||
href="https://github.com/bartvdbraak"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn-icon btn-icon-sm hover:variant-soft-primary"><GitHub /><span class="sr-only">GitHub Profile of Bart van der Braak</span></a
|
||||
class="btn-icon btn-icon-sm hover:variant-soft-primary"
|
||||
><GitHub /><span class="sr-only">GitHub Profile of Bart van der Braak</span></a
|
||||
>
|
||||
<LightSwitch />
|
||||
</svelte:fragment>
|
||||
</AppBar>
|
||||
|
||||
<ProgressBar label="Progress Bar" value={progress} max={100} rounded="" />
|
||||
<span id="progress-bar-label" class="sr-only">Loading Progress</span>
|
||||
<ProgressBar
|
||||
label="Progress Bar"
|
||||
labelledby="progress-bar-label"
|
||||
value={progress}
|
||||
max={100}
|
||||
rounded=""
|
||||
/>
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
<ul>
|
||||
{#each routes as route}
|
||||
<li class="pb-2">
|
||||
<a class="{classesActive(route.url)}" href={route.url} on:click={drawerClose}>{route.label}</a>
|
||||
<a class={classesActive(route.url)} href={route.url} on:click={drawerClose}>{route.label}</a
|
||||
>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
|
|
@ -16,11 +16,15 @@
|
|||
|
||||
<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 object-top" alt="Post" />
|
||||
<img
|
||||
src={headerImage}
|
||||
class="bg-black/50 w-full aspect-[21/9] object-cover object-top"
|
||||
alt="Post"
|
||||
/>
|
||||
</header>
|
||||
<div class="p-4 space-y-4">
|
||||
<h6 class="h6">{headerSubTitle}</h6>
|
||||
<h3 class="h3" data-toc-ignore>{title}</h3>
|
||||
<header class="h6">{headerSubTitle}</header>
|
||||
<span class="h3" data-toc-ignore>{title}</span>
|
||||
<article>
|
||||
<p>
|
||||
{description}
|
||||
|
|
|
@ -1 +1,15 @@
|
|||
<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>
|
||||
<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"
|
||||
/></svg
|
||||
>
|
||||
|
|
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 529 B |
|
@ -1 +1,18 @@
|
|||
<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>
|
||||
<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
|
||||
>
|
||||
|
|
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 352 B |
|
@ -1 +1,12 @@
|
|||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zM349.3 793.7H230.6V411.9h118.7v381.8zm-59.3-434a68.8 68.8 0 1 1 68.8-68.8c-.1 38-30.9 68.8-68.8 68.8zm503.7 434H675.1V608c0-44.3-.8-101.2-61.7-101.2-61.7 0-71.2 48.2-71.2 98v188.9H423.7V411.9h113.8v52.2h1.6c15.8-30 54.5-61.7 112.3-61.7 120.2 0 142.3 79.1 142.3 181.9v209.4z"></path></svg>
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
stroke-width="0"
|
||||
viewBox="0 0 1024 1024"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><path
|
||||
d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zM349.3 793.7H230.6V411.9h118.7v381.8zm-59.3-434a68.8 68.8 0 1 1 68.8-68.8c-.1 38-30.9 68.8-68.8 68.8zm503.7 434H675.1V608c0-44.3-.8-101.2-61.7-101.2-61.7 0-71.2 48.2-71.2 98v188.9H423.7V411.9h113.8v52.2h1.6c15.8-30 54.5-61.7 112.3-61.7 120.2 0 142.3 79.1 142.3 181.9v209.4z"
|
||||
/></svg
|
||||
>
|
||||
|
|
Before Width: | Height: | Size: 555 B After Width: | Height: | Size: 564 B |
|
@ -1 +1,14 @@
|
|||
<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>
|
||||
<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 /><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"
|
||||
/></svg
|
||||
>
|
||||
|
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
@ -1 +1,13 @@
|
|||
<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>
|
||||
<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 d="M3 19h18l-9 -15z" /></svg
|
||||
>
|
||||
|
|
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 303 B |
|
@ -5,77 +5,82 @@ const vitalsUrl = 'https://vitals.vercel-analytics.com/v1/vitals';
|
|||
|
||||
// Improve type safety by defining the navigator.connection type
|
||||
interface NavigatorWithConnection extends Navigator {
|
||||
connection: {
|
||||
effectiveType: string;
|
||||
};
|
||||
connection: {
|
||||
effectiveType: string;
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Params = Record<string, any>; // Define a type for 'params'
|
||||
|
||||
function getConnectionSpeed() {
|
||||
return 'connection' in navigator && 'connection' && 'effectiveType' in (navigator as NavigatorWithConnection).connection
|
||||
? (navigator as NavigatorWithConnection).connection.effectiveType
|
||||
: '';
|
||||
return 'connection' in navigator &&
|
||||
'connection' &&
|
||||
'effectiveType' in (navigator as NavigatorWithConnection).connection
|
||||
? (navigator as NavigatorWithConnection).connection.effectiveType
|
||||
: '';
|
||||
}
|
||||
|
||||
function sendToAnalytics(metric: Metric, options: {
|
||||
params: Params;
|
||||
path: string;
|
||||
analyticsId: string;
|
||||
debug: boolean;
|
||||
}) {
|
||||
const page = Object.entries(options.params).reduce(
|
||||
(acc, [key, value]) => acc.replace(value, `[${key}]`),
|
||||
options.path
|
||||
);
|
||||
function sendToAnalytics(
|
||||
metric: Metric,
|
||||
options: {
|
||||
params: Params;
|
||||
path: string;
|
||||
analyticsId: string;
|
||||
debug: boolean;
|
||||
}
|
||||
) {
|
||||
const page = Object.entries(options.params).reduce(
|
||||
(acc, [key, value]) => acc.replace(value, `[${key}]`),
|
||||
options.path
|
||||
);
|
||||
|
||||
const body = {
|
||||
dsn: options.analyticsId,
|
||||
id: metric.id,
|
||||
page,
|
||||
href: location.href,
|
||||
event_name: metric.name,
|
||||
value: metric.value.toString(),
|
||||
speed: getConnectionSpeed(),
|
||||
};
|
||||
const body = {
|
||||
dsn: options.analyticsId,
|
||||
id: metric.id,
|
||||
page,
|
||||
href: location.href,
|
||||
event_name: metric.name,
|
||||
value: metric.value.toString(),
|
||||
speed: getConnectionSpeed()
|
||||
};
|
||||
|
||||
if (options.debug) {
|
||||
console.log('[Web Vitals]', metric.name, JSON.stringify(body, null, 2));
|
||||
}
|
||||
if (options.debug) {
|
||||
console.log('[Web Vitals]', metric.name, JSON.stringify(body, null, 2));
|
||||
}
|
||||
|
||||
// Serialize body to a URLSearchParams object
|
||||
const searchParams = new URLSearchParams(body);
|
||||
// Serialize body to a URLSearchParams object
|
||||
const searchParams = new URLSearchParams(body);
|
||||
|
||||
// The type 'Record<string, string>' is compatible with 'URLSearchParams'
|
||||
const blob = new Blob([searchParams.toString()], {
|
||||
type: 'application/x-www-form-urlencoded',
|
||||
});
|
||||
if (navigator.sendBeacon) {
|
||||
navigator.sendBeacon(vitalsUrl, blob);
|
||||
} else {
|
||||
fetch(vitalsUrl, {
|
||||
body: blob,
|
||||
method: 'POST',
|
||||
credentials: 'omit',
|
||||
keepalive: true,
|
||||
});
|
||||
}
|
||||
// The type 'Record<string, string>' is compatible with 'URLSearchParams'
|
||||
const blob = new Blob([searchParams.toString()], {
|
||||
type: 'application/x-www-form-urlencoded'
|
||||
});
|
||||
if (navigator.sendBeacon) {
|
||||
navigator.sendBeacon(vitalsUrl, blob);
|
||||
} else {
|
||||
fetch(vitalsUrl, {
|
||||
body: blob,
|
||||
method: 'POST',
|
||||
credentials: 'omit',
|
||||
keepalive: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function webVitals(options: {
|
||||
params: Params; // Use the defined 'Params' type here
|
||||
path: string;
|
||||
analyticsId: string;
|
||||
debug: boolean;
|
||||
params: Params; // Use the defined 'Params' type here
|
||||
path: string;
|
||||
analyticsId: string;
|
||||
debug: boolean;
|
||||
}) {
|
||||
try {
|
||||
getFID((metric) => sendToAnalytics(metric, options));
|
||||
getTTFB((metric) => sendToAnalytics(metric, options));
|
||||
getLCP((metric) => sendToAnalytics(metric, options));
|
||||
getCLS((metric) => sendToAnalytics(metric, options));
|
||||
getFCP((metric) => sendToAnalytics(metric, options));
|
||||
} catch (err) {
|
||||
console.error('[Web Vitals]', err);
|
||||
}
|
||||
try {
|
||||
getFID((metric) => sendToAnalytics(metric, options));
|
||||
getTTFB((metric) => sendToAnalytics(metric, options));
|
||||
getLCP((metric) => sendToAnalytics(metric, options));
|
||||
getCLS((metric) => sendToAnalytics(metric, options));
|
||||
getFCP((metric) => sendToAnalytics(metric, options));
|
||||
} catch (err) {
|
||||
console.error('[Web Vitals]', err);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
let routes = [
|
||||
{ url: '/', label: 'Home' },
|
||||
{ url: '/projects', label: 'Projects' },
|
||||
{ url: '/tools', label: 'Tools' },
|
||||
{ url: '/tools', label: 'Tools' }
|
||||
// { url: '/blog', label: 'Blog' }
|
||||
];
|
||||
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
|
||||
<svelte:head>
|
||||
<title>hellob.art — home</title>
|
||||
<meta name="description" content="Meet Bart, a passionate DevOps engineer from Zaandam, Netherlands. With expertise in Azure, Kubernetes, and automation, he loves solving challenges through code. Discover his journey, interests in cats and whiskey, and how to connect with him for exciting collaborations.">
|
||||
<meta
|
||||
name="description"
|
||||
content="Meet Bart, a passionate DevOps engineer from Zaandam, Netherlands. With expertise in Azure, Kubernetes, and automation, he loves solving challenges through code. Discover his journey, interests in cats and whiskey, and how to connect with him for exciting collaborations."
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<main class="container mx-auto px-4 py-8 text-left">
|
||||
|
@ -38,21 +41,25 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="md:col-span-1 flex justify-end">
|
||||
<img src={picture} alt="Bart van der Braak with a noire effect" class="max-w-1/3 mb-4 object-cover" />
|
||||
<img
|
||||
src={picture}
|
||||
alt="Bart van der Braak with a noire effect"
|
||||
class="max-w-1/3 mb-4 object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<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.
|
||||
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">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
/** @type {import('./$types').PageData} */
|
||||
export let data;
|
||||
/** @type {import('./$types').PageData} */
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
@ -8,4 +8,4 @@
|
|||
</svelte:head>
|
||||
|
||||
<!-- <h1>{data.title}</h1>
|
||||
<div>{@html data.content}</div> -->
|
||||
<div>{@html data.content}</div> -->
|
||||
|
|
|
@ -49,7 +49,10 @@
|
|||
|
||||
<svelte:head>
|
||||
<title>hellob.art — projects</title>
|
||||
<meta name="description" content="Explore a diverse collection of web applications and virtual tours in the portfolio of a passionate DevOps engineer. Discover innovative projects in React, Golang, Next.js, and more.">
|
||||
<meta
|
||||
name="description"
|
||||
content="Explore a diverse collection of web applications and virtual tours in the portfolio of a passionate DevOps engineer. Discover innovative projects in React, Golang, Next.js, and more."
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<main class="container mx-auto px-4 py-8 text-left">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<script lang="ts">
|
||||
import { Canvas } from '@threlte/core';
|
||||
import { T } from '@threlte/core';
|
||||
import { ContactShadows, Float, Grid, OrbitControls } from '@threlte/extras';
|
||||
import { ContactShadows, Float, OrbitControls } from '@threlte/extras';
|
||||
import Github from './models/Github.svelte';
|
||||
import TerraformFlat from './models/TerraformFlat.svelte';
|
||||
import Kubernetes from './models/Kubernetes.svelte';
|
||||
|
@ -11,7 +11,10 @@
|
|||
|
||||
<svelte:head>
|
||||
<title>hellob.art — tools</title>
|
||||
<meta name="description" content="Explore essential DevOps tools like Kubernetes, Terraform, Warp, and version control platforms GitHub, Azure DevOps, and GitLab.">
|
||||
<meta
|
||||
name="description"
|
||||
content="Explore essential DevOps tools like Kubernetes, Terraform, Warp, and version control platforms GitHub, Azure DevOps, and GitLab."
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<main class="container mx-auto px-4 py-8 text-left">
|
||||
|
@ -56,15 +59,6 @@
|
|||
<T.DirectionalLight intensity={0.5} position.x={5} position.y={3} />
|
||||
<T.AmbientLight intensity={0.2} />
|
||||
|
||||
<!-- <Grid
|
||||
position.y={-0.001}
|
||||
cellColor="#ffffff"
|
||||
sectionColor="#ffffff"
|
||||
sectionThickness={0}
|
||||
fadeDistance={40}
|
||||
cellSize={2}
|
||||
/> -->
|
||||
|
||||
<ContactShadows scale={10} blur={2} far={2.5} opacity={0.5} />
|
||||
|
||||
<Float floatIntensity={1} floatingRange={[0, 1]}>
|
||||
|
|
|
@ -4,25 +4,25 @@ Command: npx @threlte/gltf@1.0.0-next.13 ./src/lib/assets/vectors/github.glb --t
|
|||
-->
|
||||
|
||||
<script>
|
||||
import { Group } from 'three'
|
||||
import { T, forwardEventHandlers } from '@threlte/core'
|
||||
import { useGltf } from '@threlte/extras'
|
||||
import { Group } from 'three';
|
||||
import { T, forwardEventHandlers } from '@threlte/core';
|
||||
import { useGltf } from '@threlte/extras';
|
||||
|
||||
export const ref = new Group()
|
||||
export const ref = new Group();
|
||||
|
||||
const gltf = useGltf('/models/github-transformed.glb', { useDraco: true })
|
||||
const gltf = useGltf('/models/github-transformed.glb', { useDraco: true });
|
||||
|
||||
const component = forwardEventHandlers()
|
||||
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} material={gltf.materials['SVGMat.001']} />
|
||||
{:catch error}
|
||||
<slot name="error" {error} />
|
||||
{/await}
|
||||
{#await gltf}
|
||||
<slot name="fallback" />
|
||||
{:then gltf}
|
||||
<T.Mesh geometry={gltf.nodes.Github_Mesh.geometry} material={gltf.materials['SVGMat.001']} />
|
||||
{:catch error}
|
||||
<slot name="error" {error} />
|
||||
{/await}
|
||||
|
||||
<slot {ref} />
|
||||
<slot {ref} />
|
||||
</T>
|
||||
|
|
|
@ -4,34 +4,34 @@ Command: npx @threlte/gltf@1.0.0-next.13 ./static/models/kubernetes.glb --transf
|
|||
-->
|
||||
|
||||
<script>
|
||||
import { Group } from 'three'
|
||||
import { T, forwardEventHandlers } from '@threlte/core'
|
||||
import { useGltf } from '@threlte/extras'
|
||||
import { Group } from 'three';
|
||||
import { T, forwardEventHandlers } from '@threlte/core';
|
||||
import { useGltf } from '@threlte/extras';
|
||||
|
||||
export const ref = new Group()
|
||||
export const ref = new Group();
|
||||
|
||||
const gltf = useGltf('/models/kubernetes-transformed.glb', { useDraco: true })
|
||||
const gltf = useGltf('/models/kubernetes-transformed.glb', { useDraco: true });
|
||||
|
||||
const component = forwardEventHandlers()
|
||||
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.Curve.geometry}
|
||||
material={gltf.materials['SVGMat.006']}
|
||||
rotation={[Math.PI / 2, 0, 0.9]}
|
||||
/>
|
||||
<T.Mesh
|
||||
geometry={gltf.nodes.Curve001.geometry}
|
||||
material={gltf.materials['SVGMat.007']}
|
||||
rotation={[Math.PI / 2, 0, 0.9]}
|
||||
/>
|
||||
{:catch error}
|
||||
<slot name="error" {error} />
|
||||
{/await}
|
||||
{#await gltf}
|
||||
<slot name="fallback" />
|
||||
{:then gltf}
|
||||
<T.Mesh
|
||||
geometry={gltf.nodes.Curve.geometry}
|
||||
material={gltf.materials['SVGMat.006']}
|
||||
rotation={[Math.PI / 2, 0, 0.9]}
|
||||
/>
|
||||
<T.Mesh
|
||||
geometry={gltf.nodes.Curve001.geometry}
|
||||
material={gltf.materials['SVGMat.007']}
|
||||
rotation={[Math.PI / 2, 0, 0.9]}
|
||||
/>
|
||||
{:catch error}
|
||||
<slot name="error" {error} />
|
||||
{/await}
|
||||
|
||||
<slot {ref} />
|
||||
<slot {ref} />
|
||||
</T>
|
||||
|
|
|
@ -4,28 +4,28 @@ Command: npx @threlte/gltf@1.0.0-next.13 ./static/models/terraform-flat.glb --tr
|
|||
-->
|
||||
|
||||
<script>
|
||||
import { Group } from 'three'
|
||||
import { T, forwardEventHandlers } from '@threlte/core'
|
||||
import { useGltf } from '@threlte/extras'
|
||||
import { Group } from 'three';
|
||||
import { T, forwardEventHandlers } from '@threlte/core';
|
||||
import { useGltf } from '@threlte/extras';
|
||||
|
||||
export const ref = new Group()
|
||||
export const ref = new Group();
|
||||
|
||||
const gltf = useGltf('/models/terraform-flat-transformed.glb', { useDraco: true })
|
||||
const gltf = useGltf('/models/terraform-flat-transformed.glb', { useDraco: true });
|
||||
|
||||
const component = forwardEventHandlers()
|
||||
const component = forwardEventHandlers();
|
||||
</script>
|
||||
|
||||
<T is={ref} dispose={false} {...$$restProps} bind:this={$component}>
|
||||
{#await gltf}
|
||||
<slot name="fallback" />
|
||||
{:then gltf}
|
||||
<T.Group rotation={[Math.PI / 2, 0, 5]}>
|
||||
<T.Mesh geometry={gltf.nodes.Curve_1.geometry} material={gltf.materials.SVGMat} />
|
||||
<T.Mesh geometry={gltf.nodes.Curve_2.geometry} material={gltf.materials['SVGMat.001']} />
|
||||
</T.Group>
|
||||
{:catch error}
|
||||
<slot name="error" {error} />
|
||||
{/await}
|
||||
{#await gltf}
|
||||
<slot name="fallback" />
|
||||
{:then gltf}
|
||||
<T.Group rotation={[Math.PI / 2, 0, 5]}>
|
||||
<T.Mesh geometry={gltf.nodes.Curve_1.geometry} material={gltf.materials.SVGMat} />
|
||||
<T.Mesh geometry={gltf.nodes.Curve_2.geometry} material={gltf.materials['SVGMat.001']} />
|
||||
</T.Group>
|
||||
{:catch error}
|
||||
<slot name="error" {error} />
|
||||
{/await}
|
||||
|
||||
<slot {ref} />
|
||||
<slot {ref} />
|
||||
</T>
|
||||
|
|
|
@ -4,26 +4,34 @@ Command: npx @threlte/gltf@1.0.0-next.13 ./static/models/warp.glb --transform
|
|||
-->
|
||||
|
||||
<script>
|
||||
import { Group } from 'three'
|
||||
import { T, forwardEventHandlers } from '@threlte/core'
|
||||
import { useGltf } from '@threlte/extras'
|
||||
import { Group } from 'three';
|
||||
import { T, forwardEventHandlers } from '@threlte/core';
|
||||
import { useGltf } from '@threlte/extras';
|
||||
|
||||
export const ref = new Group()
|
||||
export const ref = new Group();
|
||||
|
||||
const gltf = useGltf('/models/warp-transformed.glb', { useDraco: true })
|
||||
const gltf = useGltf('/models/warp-transformed.glb', { useDraco: true });
|
||||
|
||||
const component = forwardEventHandlers()
|
||||
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.Warp.geometry} material={gltf.materials.SVGMat} rotation={[Math.PI / 2, 0, 0.3]} />
|
||||
<T.Mesh geometry={gltf.nodes.Warp001.geometry} material={gltf.materials.Gradient} rotation={[Math.PI / 2, 0, 0.3]} />
|
||||
{:catch error}
|
||||
<slot name="error" {error} />
|
||||
{/await}
|
||||
{#await gltf}
|
||||
<slot name="fallback" />
|
||||
{:then gltf}
|
||||
<T.Mesh
|
||||
geometry={gltf.nodes.Warp.geometry}
|
||||
material={gltf.materials.SVGMat}
|
||||
rotation={[Math.PI / 2, 0, 0.3]}
|
||||
/>
|
||||
<T.Mesh
|
||||
geometry={gltf.nodes.Warp001.geometry}
|
||||
material={gltf.materials.Gradient}
|
||||
rotation={[Math.PI / 2, 0, 0.3]}
|
||||
/>
|
||||
{:catch error}
|
||||
<slot name="error" {error} />
|
||||
{/await}
|
||||
|
||||
<slot {ref} />
|
||||
<slot {ref} />
|
||||
</T>
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
|
||||
: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 */
|
||||
|
||||
}
|
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
|
|
@ -4,15 +4,29 @@ module.exports = {
|
|||
darkMode: 'class',
|
||||
content: [
|
||||
'./src/**/*.{html,js,svelte,ts}',
|
||||
require('path').join(require.resolve(
|
||||
'@skeletonlabs/skeleton'),
|
||||
'../**/*.{html,js,svelte,ts}'
|
||||
)
|
||||
require('path').join(require.resolve('@skeletonlabs/skeleton'), '../**/*.{html,js,svelte,ts}')
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
extend: {
|
||||
colors: {
|
||||
'logo-blue-start': {
|
||||
light: '#314755',
|
||||
DEFAULT: '#314755',
|
||||
dark: '#7196AD'
|
||||
},
|
||||
'logo-blue-stop': {
|
||||
light: '#26a0da',
|
||||
DEFAULT: '#26a0da',
|
||||
dark: '#7CC6E9'
|
||||
}
|
||||
},
|
||||
dark: {
|
||||
// <-- Add this section for dark mode classes
|
||||
'logo-text-gradient-dark': {
|
||||
'@apply': 'from-logo-blue-start-dark to-logo-blue-stop-dark'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
...require('@skeletonlabs/skeleton/tailwind/skeleton.cjs')()
|
||||
]
|
||||
}
|
||||
plugins: [...require('@skeletonlabs/skeleton/tailwind/skeleton.cjs')()]
|
||||
};
|
||||
|
|
|
@ -3,10 +3,10 @@ import { defineConfig } from 'vite';
|
|||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
ssr: {
|
||||
noExternal: ['three']
|
||||
},
|
||||
define: {
|
||||
'import.meta.env.VERCEL_ANALYTICS_ID': JSON.stringify(process.env.VERCEL_ANALYTICS_ID)
|
||||
}
|
||||
ssr: {
|
||||
noExternal: ['three']
|
||||
},
|
||||
define: {
|
||||
'import.meta.env.VERCEL_ANALYTICS_ID': JSON.stringify(process.env.VERCEL_ANALYTICS_ID)
|
||||
}
|
||||
});
|
||||
|
|