Compare commits

..

7 commits

Author SHA1 Message Date
0314e0bbaa
wip: update main page stuff
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-11 22:54:36 +03:30
07dc355c6c
style: add gruvbox dark & gruvbox light themes 2025-10-11 22:54:22 +03:30
e72406c6e8
build: add lucide-svelte & svelte-simple-icons as dependency 2025-10-11 22:54:01 +03:30
19b67d0b53
assets: some images 2025-10-11 22:53:26 +03:30
e629777933
feat: project component 2025-10-11 22:53:08 +03:30
a6462d8bd3
feat: theme component 2025-10-11 22:52:53 +03:30
2e7009fef2
feat: tiltcard component 2025-10-11 22:52:43 +03:30
14 changed files with 633 additions and 98 deletions

View file

@ -38,5 +38,9 @@
"typescript-eslint": "^8.20.0",
"vite": "^5.4.14",
"vitest": "^2.1.8"
},
"dependencies": {
"lucide-svelte": "^0.544.0",
"svelte-simple-icons": "^1.0.3"
}
}

75
pnpm-lock.yaml generated
View file

@ -7,6 +7,13 @@ settings:
importers:
.:
dependencies:
lucide-svelte:
specifier: ^0.544.0
version: 0.544.0(svelte@5.19.0)
svelte-simple-icons:
specifier: ^1.0.3
version: 1.0.3
devDependencies:
'@eslint/compat':
specifier: ^1.2.5
@ -16,13 +23,13 @@ importers:
version: 9.18.0
'@sveltejs/adapter-static':
specifier: ^3.0.8
version: 3.0.8(@sveltejs/kit@2.16.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.11))(svelte@5.19.0)(vite@5.4.11))
version: 3.0.8(@sveltejs/kit@2.16.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.19))(svelte@5.19.0)(vite@5.4.19))
'@sveltejs/kit':
specifier: ^2.0.0
version: 2.16.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.11))(svelte@5.19.0)(vite@5.4.11)
version: 2.16.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.19))(svelte@5.19.0)(vite@5.4.19)
'@sveltejs/vite-plugin-svelte':
specifier: ^4.0.0
version: 4.0.4(svelte@5.19.0)(vite@5.4.11)
version: 4.0.4(svelte@5.19.0)(vite@5.4.19)
'@tailwindcss/container-queries':
specifier: ^0.1.1
version: 0.1.1(tailwindcss@3.4.17)
@ -72,8 +79,8 @@ importers:
specifier: ^8.20.0
version: 8.20.0(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3)
vite:
specifier: ^5.4.11
version: 5.4.11
specifier: ^5.4.14
version: 5.4.19
vitest:
specifier: ^2.1.8
version: 2.1.8
@ -1049,6 +1056,11 @@ packages:
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
lucide-svelte@0.544.0:
resolution: {integrity: sha512-8kBxSivf8SJdEUJRHBpu9bRw0S/qfVK+Yfb92KQnRRBdP425RzT6aQfrIfZctG1oucPVTBQe1ZXgmth/3qVICg==}
peerDependencies:
svelte: ^3 || ^4 || ^5.0.0-next.42
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
@ -1449,6 +1461,9 @@ packages:
svelte:
optional: true
svelte-simple-icons@1.0.3:
resolution: {integrity: sha512-BmQPPrV9fHMH+BJSf4xBKNP0oW2XCtWizZnfAnlOBn54UWRLWmk8uTt0JRAF5NfS+74DD09oHADAroneC94eFg==}
svelte@5.19.0:
resolution: {integrity: sha512-qvd2GvvYnJxS/MteQKFSMyq8cQrAAut28QZ39ySv9k3ggmhw4Au4Rfcsqva74i0xMys//OhbhVCNfXPrDzL/Bg==}
engines: {node: '>=18'}
@ -1539,8 +1554,8 @@ packages:
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
vite@5.4.11:
resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==}
vite@5.4.19:
resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@ -1878,13 +1893,13 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.30.1':
optional: true
'@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.16.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.11))(svelte@5.19.0)(vite@5.4.11))':
'@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.16.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.19))(svelte@5.19.0)(vite@5.4.19))':
dependencies:
'@sveltejs/kit': 2.16.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.11))(svelte@5.19.0)(vite@5.4.11)
'@sveltejs/kit': 2.16.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.19))(svelte@5.19.0)(vite@5.4.19)
'@sveltejs/kit@2.16.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.11))(svelte@5.19.0)(vite@5.4.11)':
'@sveltejs/kit@2.16.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.19))(svelte@5.19.0)(vite@5.4.19)':
dependencies:
'@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.19.0)(vite@5.4.11)
'@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.19.0)(vite@5.4.19)
'@types/cookie': 0.6.0
cookie: 0.6.0
devalue: 5.1.1
@ -1897,27 +1912,27 @@ snapshots:
set-cookie-parser: 2.7.1
sirv: 3.0.0
svelte: 5.19.0
vite: 5.4.11
vite: 5.4.19
'@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.11))(svelte@5.19.0)(vite@5.4.11)':
'@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.19))(svelte@5.19.0)(vite@5.4.19)':
dependencies:
'@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.19.0)(vite@5.4.11)
'@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.19.0)(vite@5.4.19)
debug: 4.4.0
svelte: 5.19.0
vite: 5.4.11
vite: 5.4.19
transitivePeerDependencies:
- supports-color
'@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.11)':
'@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.19)':
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.11))(svelte@5.19.0)(vite@5.4.11)
'@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.0)(vite@5.4.19))(svelte@5.19.0)(vite@5.4.19)
debug: 4.4.0
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.17
svelte: 5.19.0
vite: 5.4.11
vitefu: 1.0.5(vite@5.4.11)
vite: 5.4.19
vitefu: 1.0.5(vite@5.4.19)
transitivePeerDependencies:
- supports-color
@ -2025,13 +2040,13 @@ snapshots:
chai: 5.1.2
tinyrainbow: 1.2.0
'@vitest/mocker@2.1.8(vite@5.4.11)':
'@vitest/mocker@2.1.8(vite@5.4.19)':
dependencies:
'@vitest/spy': 2.1.8
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
vite: 5.4.11
vite: 5.4.19
'@vitest/pretty-format@2.1.8':
dependencies:
@ -2541,6 +2556,10 @@ snapshots:
lru-cache@10.4.3: {}
lucide-svelte@0.544.0(svelte@5.19.0):
dependencies:
svelte: 5.19.0
magic-string@0.30.17:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
@ -2861,6 +2880,8 @@ snapshots:
optionalDependencies:
svelte: 5.19.0
svelte-simple-icons@1.0.3: {}
svelte@5.19.0:
dependencies:
'@ampproject/remapping': 2.3.0
@ -2978,7 +2999,7 @@ snapshots:
debug: 4.4.0
es-module-lexer: 1.6.0
pathe: 1.1.2
vite: 5.4.11
vite: 5.4.19
transitivePeerDependencies:
- '@types/node'
- less
@ -2990,7 +3011,7 @@ snapshots:
- supports-color
- terser
vite@5.4.11:
vite@5.4.19:
dependencies:
esbuild: 0.21.5
postcss: 8.5.1
@ -2998,14 +3019,14 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
vitefu@1.0.5(vite@5.4.11):
vitefu@1.0.5(vite@5.4.19):
optionalDependencies:
vite: 5.4.11
vite: 5.4.19
vitest@2.1.8:
dependencies:
'@vitest/expect': 2.1.8
'@vitest/mocker': 2.1.8(vite@5.4.11)
'@vitest/mocker': 2.1.8(vite@5.4.19)
'@vitest/pretty-format': 2.1.8
'@vitest/runner': 2.1.8
'@vitest/snapshot': 2.1.8
@ -3021,7 +3042,7 @@ snapshots:
tinyexec: 0.3.2
tinypool: 1.0.2
tinyrainbow: 1.2.0
vite: 5.4.11
vite: 5.4.19
vite-node: 2.1.8
why-is-node-running: 2.3.0
transitivePeerDependencies:

View file

@ -1,79 +1,97 @@
<div>
<span class="text-2xl">Portfolio under construction...</span>
<br />
<script>
import Project from './project.svelte';
import ThemeToggle from './theme.svelte';
import TiltCard from './tiltcard.svelte';
If you felt disappointed, let it be a practice to control your expectations... But I promise to
tidy this place up soon!!! :D
<br />
// Assuming icons for Github, Gitlab, Codeberg (Code), Forgejo (GitBranch)
import { Github, Gitlab, Code, GitBranch } from 'lucide-svelte';
Anyways, here are some links to all my <span class="text-accent_text"> social handles </span>:
<br />
<br />
const repository = {
label: 'Forge',
url: 'https://git.light7734.com/light7734/light',
iconComponent: GitBranch
};
Mail:
<a href="mailto:light7734@tuta.io" class="text-blue"> light7734@tuta.io </a>
<br />
const mirrors = [
{ label: 'Github', url: 'https://github.com/light7734/light', iconComponent: Github },
{ label: 'Gitlab', url: 'https://gitlab.com/light7734/light', iconComponent: Gitlab },
{ label: 'Codeberg', url: 'https://codeberg.org/light7734/light', iconComponent: Code }
];
Youtube:
<a href="https://www.youtube.com/@light.7734" class="text-blue"> @light.7734 </a>
<br />
const gallery = [
'https://placekittens.com/720/480',
'https://placekittens.com/720/481',
'https://placekittens.com/720/482',
'https://placekittens.com/720/483',
'https://placekittens.com/720/484',
'https://placekittens.com/720/484'
];
Twitter:
<a href="https://x.com/light7734" class="text-blue"> @light7734 </a>
<br />
const features = ['MSAA', 'SSAO', 'PBR Lighting'];
Bluesky:
<a href="https://bsky.app/profile/light7734.bsky.social" class="text-blue"> @light7734 </a>
<br />
const languages = [{ name: 'C++23', icon: 'cplusplus.svg' }, { name: 'CMake' }];
Reddit:
<a href="https://www.reddit.com/user/Light7734/" class="text-blue"> u/light7734 </a>
<br />
const graphicsApis = ['Vulkan', 'Metal', 'DirectX12'];
Itcho.io:
<a href="https://light7734.itch.io/" class="text-blue"> light7734 </a>
<br />
const cicd = ['Drone', 'Docker'];
</script>
Discord: @light7734
<br />
<div
class="flex min-h-screen bg-gruvboxLight-bg text-gruvboxLight-fg transition-colors duration-300 dark:bg-gruvboxDark-bg dark:text-gruvboxDark-fg"
>
<div class="flex-1 bg-[#1d2021]"></div>
<main class="bg-card relative flex-[10] p-8 transition-all duration-300 ease-out">
<div class="absolute right-0 top-0">
<ThemeToggle />
</div>
Discord Community: @light7734
<br />
<h1>Bio</h1>
Telegram Dailies: soon
<br />
<br />
Instagram: soon
<br />
<Project
title="LIGHT"
description_preview="Dependency free, cross-platform, cross-graphics-api, bleeding-edge game engine."
description="Dependency free, cross-platform, cross-graphics-api, bleeding-edge game engine."
icon="/light.svg"
{repository}
{mirrors}
{gallery}
{features}
{languages}
{graphicsApis}
{cicd}
/>
Forgejo (git repos):
<a href="https://git.light7734.com/light7734" class="text-blue"> git.light7734.com </a>
<br />
Codeberg (mirrors):
<a href="https://codeberg.org/light7734" class="text-blue"> @light7734 </a>
<br />
Github (mirrors):
<a href="https://codeberg.org/light7734" class="text-blue"> @light7734 </a>
<br />
Gitlab (mirrors):
<a href="https://gitlab.com/Light7734" class="text-blue"> @light7734 </a>
<br />
<br />
<Project
title="LIGHT"
description_preview="Dependency free, cross-platform, cross-graphics-api, bleeding-edge game engine."
description="Detailed description here."
icon="/light.svg"
{repository}
{mirrors}
{gallery}
{features}
{languages}
{graphicsApis}
{cicd}
/>
</main>
<div class="flex-1 bg-[#1d2021]"></div>
</div>
<!-- Brief Bio
-->
<!--OPEN SOURCE PROJECTS -->
<!-- Brief Dazzle
<!-- Description
I love teaching! I've learned that it's the most effective way to solidify your knowledge and leave
little to no gaps. Dazzle is the collection of my articles teaching a subject in depth. It delves
into topics such as rendering, mathematics, guidelines and more. It's just my way of giving back to
the community!
-->
<!-- Gallery
<...images...>
-->
<!-- Technical Details
Technology behind Dazzle:
@ -107,9 +125,6 @@ Technology behind Light:
CICD:
Drone, Docker,
Prominent vendor libs:
EnTT, Glfw3.4, Dear (truly a dear) ImGui,
Check out the source code on any of the official mirrors or on the main self-hosted repo.
Self-hosted with <3 using Forgejo (a fork of Gittea)
CodeBerg mirror

192
src/routes/project.svelte Normal file
View file

@ -0,0 +1,192 @@
<script lang="ts">
import { ChevronDown, ChevronUp, ExternalLink } from 'lucide-svelte';
import TiltCard from './tiltcard.svelte';
import { Github, Gitlab, Code, GitBranch } from 'lucide-svelte';
interface ProjectLink {
label: string;
url: string;
iconComponent: any;
}
interface Language {
name: string;
icon?: string;
}
export let title: string;
export let description_preview: string;
export let description: string;
export let icon: string;
export let repository: ProjectLink = { label: 'Forgejo', url: '', iconComponent: GitBranch };
export let mirrors: ProjectLink[] = [
{ label: 'Github', url: '', iconComponent: Github },
{ label: 'Gitlab', url: '', iconComponent: Gitlab },
{ label: 'Codeberg', url: '', iconComponent: Code }
];
export let gallery: string[] = [];
export let features: string[] = [];
export let languages: Language[] = [];
export let graphicsApis: string[] = [];
export let cicd: string[] = [];
let expansionStage = 0; // 0: collapsed, 1: description, 2: gallery, 3: source code
const MAX_STAGE = 3;
function toggleExpansion() {
expansionStage = expansionStage >= MAX_STAGE ? 0 : expansionStage + 1;
}
import { slide } from 'svelte/transition';
</script>
<div
class="bg-card border-border relative mx-auto w-full max-w-2xl rounded-lg border transition-all duration-300 ease-out hover:scale-[1.01] hover:shadow-[5px_5px_5px_#000000]"
>
<div class="p-6">
<div class="mb-4 flex items-center gap-4">
<div class="flex-shrink-0">
<TiltCard imageSrc={icon} imageAlt="{title} icon" width="256px" height="256px" />
</div>
<div class="min-w-0 flex-1">
<TiltCard imageSrc="/light_text.svg" imageAlt="{title} icon" width="auto" height="auto" />
</div>
</div>
<p class="text-muted-foreground leading-relaxed">
{expansionStage >= 1 ? description : description_preview}
</p>
{#if expansionStage >= 1}
<div class="py-4" transition:slide={{ duration: 300 }}>
{#if features.length > 0 || languages.length > 0 || graphicsApis.length > 0 || cicd.length > 0}
{#if features.length > 0}
<h3 class="text-muted-foreground mb-3 text-sm font-semibold uppercase tracking-wide">
Features
</h3>
<ul class="text-muted-foreground mb-2 list-disc pl-5">
{#each features as feature}
<li>{feature}</li>
{/each}
</ul>
{/if}
<h4 class="mb-1 font-medium">Technology behind {title}:</h4>
{#if languages.length > 0}
<h5 class="mb-1 text-sm">Languages:</h5>
<div class="mb-2 flex flex-wrap gap-2">
{#each languages as lang}
<span class="flex items-center gap-1">
{#if lang.icon}
<img src={lang.icon} alt="{lang.name} icon" class="ivert h-4 w-4" />
{/if}
{lang.name}
</span>
{/each}
</div>
{/if}
{#if graphicsApis.length > 0}
<h5 class="mb-1 text-sm">Graphics APIs:</h5>
<div class="mb-2 flex flex-wrap gap-2">
{#each graphicsApis as api}
<span>{api}</span>
{/each}
</div>
{/if}
{#if cicd.length > 0}
<h5 class="mb-1 text-sm">CICD:</h5>
<div class="mb-2 flex flex-wrap gap-2">
{#each cicd as tool}
<span>{tool}</span>
{/each}
</div>
{/if}
{/if}
</div>
{/if}
{#if expansionStage >= 2 && gallery.length > 0}
<div transition:slide={{ duration: 300 }}>
<h3 class="text-muted-foreground mb-3 text-sm font-semibold uppercase tracking-wide">
Gallery
</h3>
<div class="mb-4 grid grid-cols-3 gap-2">
{#each gallery as img}
<img src={img} alt="{title} screenshot" class="rounded object-cover" />
{/each}
</div>
</div>
{/if}
{#if expansionStage >= 3}
<div transition:slide={{ duration: 300 }}>
<div class="border-border border-t pt-4">
<div class="flex items-start justify-start gap-0">
<div class="flex-none">
<h3 class="text-muted-foreground mb-2 text-sm font-semibold uppercase tracking-wide">
Repository
</h3>
<div class="flex flex-wrap gap-3">
<a
href={repository.url}
target="_blank"
rel="noopener noreferrer"
class="text-accent-foreground flex items-center gap-1 transition-colors duration-200 hover:underline"
>
<svelte:component this={repository.iconComponent} class="h-4 w-4" />
{repository.label}
</a>
</div>
</div>
<div class="border-border mx-4 h-8 self-center border-l"></div>
<div class="flex-none">
<h3 class="text-muted-foreground mb-2 text-sm font-semibold uppercase tracking-wide">
Mirrors
</h3>
<div class="flex flex-wrap gap-3">
{#each mirrors as link}
<a
href={link.url}
target="_blank"
rel="noopener noreferrer"
class="text-accent-foreground flex items-center gap-1 transition-colors duration-200 hover:underline"
>
<svelte:component this={link.iconComponent} class="h-4 w-4" />
{link.label}
</a>
{/each}
</div>
</div>
</div>
</div>
</div>
{/if}
</div>
<button
on:click={toggleExpansion}
class="bg-muted/50 hover:bg-muted text-muted-foreground hover:text-foreground border-border group flex w-full items-center justify-center gap-2 rounded-b-lg border-t px-6 py-3 transition-colors duration-200"
>
{#if expansionStage >= MAX_STAGE}
<ChevronUp
class="h-4 w-16 transition-transform duration-200 group-hover:translate-y-[-2px]"
/>
{:else}
<ChevronDown
class="h-30 w-30 transition-transform duration-200 group-hover:translate-y-[2px]"
/>
<span class="font-bold">
{#if expansionStage === 0}
FEATURES
{:else if expansionStage === 1}
GALLERY
{:else if expansionStage === 2}
SOURCE
{/if}
</span>
<ChevronDown
class="h-30 w-30 transition-transform duration-200 group-hover:translate-y-[2px]"
/>
{/if}
</button>
</div>

41
src/routes/theme.svelte Normal file
View file

@ -0,0 +1,41 @@
<script lang="ts">
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
import { Sun, Moon } from 'lucide-svelte';
const theme = writable<'light' | 'dark'>('light');
onMount(() => {
// Load from localStorage if exists
const stored = localStorage.getItem('theme');
if (stored === 'dark') {
document.documentElement.classList.add('dark');
theme.set('dark');
}
});
function toggleTheme() {
theme.update((current) => {
const next = current === 'light' ? 'dark' : 'light';
if (next === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
localStorage.setItem('theme', next);
return next;
});
}
</script>
<button
on:click={toggleTheme}
class="text-gruvboxLight-fg dark:text-gruvboxDark-fg rounded-md px-4 py-2 transition-all duration-300 ease-out hover:scale-[1.05] hover:drop-shadow-[2px_2px_1px_#000000]"
>
{#if $theme === 'light'}
<Moon class="size-8" />
{/if}
{#if $theme === 'dark'}
<Sun class="size-8" />
{/if}
</button>

View file

@ -0,0 +1,74 @@
<script lang="ts">
export let imageSrc: string;
export let imageAlt: string = 'Card image';
export let width: string = '300px';
export let height: string = '400px';
let cardElement: HTMLDivElement;
let rotateX = 0;
let rotateY = 0;
let isHovered = false;
// Derived shadow offsets
let shadowX = 0;
let shadowY = 0;
let shadowBlur = 20;
function handleMouseMove(event: MouseEvent) {
if (!cardElement) return;
const rect = cardElement.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
// Calculate rotation based on cursor position (max tilt of 10 degrees)
rotateY = ((x - centerX) / centerX) * 10 * 2;
rotateX = ((centerY - y) / centerY) * 10 * 2;
// Make shadow move opposite to tilt for a realistic lighting effect
shadowX = -rotateY * 0.7; // exaggerate slightly
shadowY = rotateX * 0.7;
}
function handleMouseEnter() {
isHovered = true;
}
function handleMouseLeave() {
isHovered = false;
rotateX = 0;
rotateY = 0;
shadowX = 0;
shadowY = 0;
}
</script>
<div
bind:this={cardElement}
class="cursor-pointer"
class:scale-[1.03]={isHovered}
style="
width: {width};
height: {height};
perspective: 1000px;
filter: drop-shadow({shadowX}px {shadowY}px 0px rgba(0, 0, 0, 0.5));
"
on:mousemove={handleMouseMove}
on:mouseenter={handleMouseEnter}
on:mouseleave={handleMouseLeave}
role="img"
aria-label={imageAlt}
>
<img
src={imageSrc}
alt={imageAlt}
class="h-full w-full rounded-xl object-cover transition-transform duration-100 ease-out"
style="
transform: rotateX({rotateX}deg) rotateY({rotateY}deg);
transform-style: preserve-3d;
"
/>
</div>

1
static/cplusplus.svg Normal file
View file

@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>C++</title><path d="M22.394 6c-.167-.29-.398-.543-.652-.69L12.926.22c-.509-.294-1.34-.294-1.848 0L2.26 5.31c-.508.293-.923 1.013-.923 1.6v10.18c0 .294.104.62.271.91.167.29.398.543.652.69l8.816 5.09c.508.293 1.34.293 1.848 0l8.816-5.09c.254-.147.485-.4.652-.69.167-.29.27-.616.27-.91V6.91c.003-.294-.1-.62-.268-.91zM12 19.11c-3.92 0-7.109-3.19-7.109-7.11 0-3.92 3.19-7.11 7.11-7.11a7.133 7.133 0 016.156 3.553l-3.076 1.78a3.567 3.567 0 00-3.08-1.78A3.56 3.56 0 008.444 12 3.56 3.56 0 0012 15.555a3.57 3.57 0 003.08-1.778l3.078 1.78A7.135 7.135 0 0112 19.11zm7.11-6.715h-.79v.79h-.79v-.79h-.79v-.79h.79v-.79h.79v.79h.79zm2.962 0h-.79v.79h-.79v-.79h-.79v-.79h.79v-.79h.79v.79h.79z"/></svg>

After

Width:  |  Height:  |  Size: 764 B

57
static/drawing.svg Normal file
View file

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="256"
viewBox="0 0 135.46667 67.733334"
version="1.1"
id="svg1"
inkscape:export-filename="light_text.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
sodipodi:docname="drawing.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="4.1228676"
inkscape:cx="335.20359"
inkscape:cy="230.30087"
inkscape:window-width="2560"
inkscape:window-height="1368"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:39.5424px;font-family:'Noto Sans Elbasan';-inkscape-font-specification:'Noto Sans Elbasan';text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#e21a41;fill-opacity:1;stroke:#000000;stroke-width:13.1808;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0"
x="12.127329"
y="50.100418"
id="text1"
transform="scale(1.0765584,0.92888597)"><tspan
sodipodi:role="line"
id="tspan1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'XP Ziba';-inkscape-font-specification:'XP Ziba';fill:#e21a41;fill-opacity:1;stroke:#000000;stroke-width:13.1808;stroke-opacity:0"
x="12.127329"
y="50.100418">LIGHT</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
static/light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

79
static/light.svg Normal file
View file

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="200"
height="200"
viewBox="0 0 52.916667 52.916669"
version="1.1"
id="svg5998"
sodipodi:docname="light.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
inkscape:export-filename="light.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview10"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="1.1083899"
inkscape:cx="153.37563"
inkscape:cy="221.94356"
inkscape:window-width="1130"
inkscape:window-height="1269"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="svg5998">
<inkscape:grid
type="xygrid"
id="grid2988"
originx="0"
originy="0"
spacingy="1"
spacingx="1"
units="mm" />
</sodipodi:namedview>
<defs
id="defs5995" />
<g
id="g3561"
transform="matrix(1.0702951,0,0,1.0725059,5.8605666,5.8179937)">
<path
style="fill:none;stroke:#e21a41;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers;stop-color:#000000"
d="M 19.245009,12.226594 9.8870095,0.52916664 H 28.602909 Z"
id="path1118"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#e21a41;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers;stop-color:#000000"
d="M 19.245009,19.245046 9.8870095,7.5476166 0.52910948,19.245046 c 6.23860002,0 18.83849952,-1.05e-4 18.71589952,0 z"
id="path1120"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#e21a41;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers;stop-color:#000000"
d="M 0.52910948,19.245046 19.245009,37.960915 V 19.245046 Z"
id="path1122"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#e21a41;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers;stop-color:#000000"
d="m 37.960809,19.245046 -18.7158,18.715869 V 19.245046 Z"
id="path1124"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#e21a41;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.8;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers;stop-color:#000000"
d="m 37.960809,19.245046 -9.3579,-11.6974294 -9.3579,11.6974294 c 6.2386,0 18.8385,-1.05e-4 18.7158,0 z"
id="path1126"
sodipodi:nodetypes="cccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
static/light_banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

28
static/light_text.svg Normal file
View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="256"
viewBox="0 0 135.46667 67.733334"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<g
id="layer1">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:39.5424px;font-family:'Noto Sans Elbasan';-inkscape-font-specification:'Noto Sans Elbasan';text-align:start;writing-mode:lr-tb;direction:ltr;text-anchor:start;fill:#e21a41;fill-opacity:1;stroke:#000000;stroke-width:13.1808;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0"
x="12.127329"
y="50.100418"
id="text1"
transform="scale(1.0765584,0.92888597)"><tspan
id="tspan1"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'XP Ziba';-inkscape-font-specification:'XP Ziba';fill:#e21a41;fill-opacity:1;stroke:#000000;stroke-width:13.1808;stroke-opacity:0"
x="12.127329"
y="50.100418">LIGHT</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
static/pfp.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

View file

@ -3,28 +3,51 @@ import typography from '@tailwindcss/typography';
import type { Config } from 'tailwindcss';
export default {
darkMode: 'class', // important for theme switching
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {},
colors: {
foundation: '#282828',
primary: '#282828',
secondary: '#000000',
accent: '#ff0000',
extend: {
colors: {
// Gruvbox Light
gruvboxLight: {
bg: '#fbf1c7',
fg: '#3c3836',
primary: '#458588',
secondary: '#b16286',
accent: '#d79921',
neutral: '#a89984',
muted: '#7c6f64',
red: '#cc241d',
green: '#98971a',
blue: '#458588',
yellow: '#d79921',
orange: '#d65d0e',
purple: '#b16286',
aqua: '#689d6a'
},
neutral: '#000000',
primary_text: '#fbf1c7',
muted_text: '#a89984',
accent_text: '#fb4934',
red: '#000000',
green: '#000000',
blue: '#83a598',
yellow: '#000000'
// Gruvbox Dark
gruvboxDark: {
bg: '#282828',
fg: '#ebdbb2',
primary: '#83a598',
secondary: '#d3869b',
accent: '#fabd2f',
neutral: '#928374',
muted: '#7c6f64',
red: '#fb4934',
green: '#b8bb26',
blue: '#83a598',
yellow: '#fabd2f',
orange: '#fe8019',
purple: '#d3869b',
aqua: '#8ec07c'
}
}
}
},
plugins: [typography, containerQueries]
} satisfies Config;