Compare commits

..

15 Commits

Author SHA1 Message Date
BackwardsUser
1d9b72d161 Removed github actions 2025-04-26 12:03:57 -04:00
BackwardsUser
9b9c3a9a07 This actually did need to be node. 2025-04-26 12:01:09 -04:00
Backwards
3d4a8d7bea ci: add Azure Static Web Apps workflow file
on-behalf-of: @Azure opensource@microsoft.com
2025-04-26 11:58:30 -04:00
BackwardsUser
2c6b0bea94 Changed sveltekit adapter to static 2025-04-26 11:57:13 -04:00
BackwardsUser
34a6ea0e87 Fixed Brands missing from small client and added "schedule meeting" "brand" 2025-04-26 11:45:49 -04:00
BackwardsUser
841227bb1c Discord Avatar retriever wasn't being error handled... 2025-04-26 11:22:13 -04:00
BackwardsUser
916ffe5a0e Added my Resume to " brands\ 2025-03-20 19:50:51 -04:00
BackwardsUser
5efefe7751 Added small device view 2025-03-05 01:55:52 -05:00
BackwardsUser
7e778ea547 Synced Sidebars (desync was bad design choice) 2025-03-04 22:10:15 -05:00
BackwardsUser
4b529f6f5b Everything went dark... idk what happened 2025-03-04 22:01:38 -05:00
480760ed5a Justified Brands 'space-between' 2025-03-04 16:02:34 -05:00
c842a38c41 Initial Commit 2025-03-04 15:30:52 -05:00
428910d835 testing auto prod 2025-02-26 23:22:38 +00:00
6a547289f7 Changed build directory 2025-02-26 23:20:52 +00:00
Harding
2ac634bb3f Removed README 2025-02-26 15:45:34 -05:00
27 changed files with 523 additions and 464 deletions

View File

@ -1,38 +0,0 @@
# sv
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npx sv create
# create a new project in my-app
npx sv create my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.

146
package-lock.json generated
View File

@ -22,11 +22,14 @@
"@skeletonlabs/skeleton": "^2.10.4",
"@skeletonlabs/tw-plugin": "^0.4.0",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-node": "^5.2.12",
"@sveltejs/adapter-node": "^5.2.11",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.4",
"@tailwindcss/forms": "^0.5.10",
"autoprefixer": "^10.4.20",
"axios": "^1.8.1",
"dylan-ipsum": "^1.1.0",
"eslint": "^9.7.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0",
@ -1374,9 +1377,9 @@
}
},
"node_modules/@sveltejs/adapter-node": {
"version": "5.2.12",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.12.tgz",
"integrity": "sha512-0bp4Yb3jKIEcZWVcJC/L1xXp9zzJS4hDwfb4VITAkfT4OVdkspSHsx7YhqJDbb2hgLl6R9Vs7VQR+fqIVOxPUQ==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.11.tgz",
"integrity": "sha512-lR7/dfUaKFf3aI408KRDy/BVDYoqUws7zNOJz2Hl4JoshlTnMgdha3brXBRFXB+cWtYvJjjPhvmq3xqpbioi4w==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -1389,6 +1392,16 @@
"@sveltejs/kit": "^2.4.0"
}
},
"node_modules/@sveltejs/adapter-static": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz",
"integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@sveltejs/kit": "^2.0.0"
}
},
"node_modules/@sveltejs/kit": {
"version": "2.15.2",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.15.2.tgz",
@ -1655,6 +1668,13 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true,
"license": "MIT"
},
"node_modules/autoprefixer": {
"version": "10.4.20",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
@ -1693,6 +1713,18 @@
"postcss": "^8.1.0"
}
},
"node_modules/axios": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz",
"integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==",
"dev": true,
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@ -1983,6 +2015,19 @@
"dev": true,
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@ -2159,6 +2204,16 @@
"node": ">=0.10.0"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@ -2213,6 +2268,13 @@
"node": ">= 0.4"
}
},
"node_modules/dylan-ipsum": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/dylan-ipsum/-/dylan-ipsum-1.1.0.tgz",
"integrity": "sha512-L/U6bSf63eovEtddaJTRyuG42/1Nq5R7blYOyoAD8NXPw+EimuynIL9Sgh7F1msVwlmvCAFdpeh6wXjJuQg76A==",
"dev": true,
"license": "MIT"
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@ -2279,6 +2341,22 @@
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
@ -2826,6 +2904,27 @@
"dev": true,
"license": "ISC"
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/foreground-child": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
@ -2843,6 +2942,22 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -3057,6 +3172,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@ -4258,6 +4389,13 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"dev": true,
"license": "MIT"
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",

View File

@ -21,11 +21,14 @@
"@skeletonlabs/skeleton": "^2.10.4",
"@skeletonlabs/tw-plugin": "^0.4.0",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-node": "^5.2.12",
"@sveltejs/adapter-node": "^5.2.11",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.4",
"@tailwindcss/forms": "^0.5.10",
"autoprefixer": "^10.4.20",
"axios": "^1.8.1",
"dylan-ipsum": "^1.1.0",
"eslint": "^9.7.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0",

View File

@ -1,13 +1,4 @@
/* Test */
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
* {
overflow: hidden !important;
}
*:not(a),
*:not(button),
*:not(input) {
user-select: none;
}
@import 'tailwindcss/utilities';

View File

@ -1,45 +0,0 @@
<script>
import Hero from "$lib/components/Hero.svelte";
import { crossfade, fade, fly } from "svelte/transition";
import { onMount } from "svelte";
import Up from "$lib/components/navigation-items/Up.svelte";
import Left from "$lib/components/navigation-items/Left.svelte";
import Right from "$lib/components/navigation-items/Right.svelte";
import Down from "$lib/components/navigation-items/Down.svelte";
export let scrollInDirection;
let isAtTop = true;
onMount(() => {
const handleScroll = () => {
isAtTop = window.scrollY === 0;
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
});
</script>
<header id="header" class="h-screen grid p-4 hero">
<div class="spacer"></div>
<Up {scrollInDirection} />
<div class="spacer"></div>
<Left {scrollInDirection} />
<Hero></Hero>
<Right {scrollInDirection} />
<div class="spacer"></div>
<Down {scrollInDirection} />
</header>
<style>
:global(html) {
scroll-snap-type: y mandatory;
scroll-behavior: smooth;
}
#header {
grid-template-areas: '. . .'
'. . .'
'. . .';
}
</style>

View File

@ -1,44 +0,0 @@
<script>
import BackwardsLogo from "$lib/assets/Backwards.png";
import MdiGithub from '~icons/mdi/github';
import MdiYoutube from '~icons/mdi/youtube';
import MdiDiscord from '~icons/mdi/discord';
import MdiLinkedin from '~icons/mdi/linkedin';
import CibGitea from '~icons/cib/gitea';
let personal = true;
</script>
<div class="text-center h-max self-center" id="hero">
{#if personal}
<h1 class="h1 overflow-hidden">Alexander Harding</h1>
<h3 class="h3">Software and Application Developer</h3>
{:else}
<h1 class="h1 overflow-hidden">Backwards Development</h1>
<h4 class="h4">A Software Development and Distribution Company</h4>
{/if}
<div class="flex justify-evenly pt-4">
<a href="https://github.com/BackwardsUser/" class="btn variant-filled-surface text-2xl p-3 rounded-md spin">
<span class="block spinner duration-500"><MdiGithub /></span>
</a>
<a href="https://git.backwards.dev/" class="btn variant-filled-surface text-2xl p-3 rounded-md spin">
<span class="block spinner duration-500"><CibGitea /></span>
</a>
<a href="https://www.linkedin.com/in/alexander-harding-71b661265/" class="btn variant-filled-surface text-2xl p-3 rounded-md spin">
<span class="block spinner duration-500"><MdiLinkedin /></span>
</a>
<a href="https://www.youtube.com/@BackwardsDevelopment" class="btn variant-filled-surface text-2xl p-3 rounded-md spin">
<span class="block spinner duration-500"><MdiYoutube /></span>
</a>
<a href="https://discord.gg/Zhq9yjhHKr" class="btn variant-filled-surface text-2xl p-3 rounded-md spin">
<span class="block spinner duration-500"><MdiDiscord /></span>
</a>
</div>
</div>
<style>
.spin:hover>.spinner {
transform: rotateZ(360deg);
}
</style>

View File

@ -1,36 +0,0 @@
<script>
import MaterialSymbolsArrowForwardRounded from "~icons/material-symbols/arrow-forward-rounded";
import MaterialSymbolsLabProfileOutline from '~icons/material-symbols/lab-profile-outline';
import Down from "./navigation-items/Down.svelte";
export let scrollInDirection;
let mediaPageOnline = false;
</script>
<div id="media" class="w-screen h-screen flex flex-col justify-between items-center">
<div class="spacer"></div>
<div class="flex flex-col justify-center items-center">
<h2 class="h2 font-bold">Backwards Media</h2>
<h3 class="h3">Upload and Share Media with your Friends</h3>
<div class="flex flex-row controls pt-4 gap-12">
<a href={mediaPageOnline ? "https://media.backwards.dev/" : ""} class="btn variant-filled-primary cool-hover opacity-50 cursor-not-allowed">
<span>Try it out</span>
<span class="effect duration-500 ease-in-out rounded-xl"><MaterialSymbolsArrowForwardRounded /></span>
</a>
<a href="/about#media" class="btn variant-ghost-surface cool-hover">
<span>Learn more</span>
<span class="effect duration-500 ease-in-out rounded-xl"><MaterialSymbolsLabProfileOutline /></span>
</a>
</div>
</div>
<div class="wrapper">
<Down {scrollInDirection} />
</div>
</div>
<style>
/* dumb ass name :(, cool ass animation :) */
.cool-hover:hover>.effect {
transform: rotateZ(360deg);
}
</style>

View File

@ -1,55 +0,0 @@
<script>
import { Avatar } from "@skeletonlabs/skeleton";
import projects from "$lib/data/projects.json";
import { onMount } from "svelte";
import Up from "./navigation-items/Up.svelte";
export let scrollInDirection;
const fallbackThumbnail = "/Image_not_available.png"; // Set a valid default image path
function handleImageError(event, type) {
if (type === "thumbnail") {
event.target.src = fallbackThumbnail;
}
}
</script>
<div id="projects" class="min-h-screen">
<div class="wrapper">
<Up {scrollInDirection} />
<h2 class="h2 text-center w-screen pt-4 overflow-hidden">Projects</h2>
<div class="flex flex-wrap gap-4 p-4 w-max max-w-full m-auto">
{#each projects as project}
<a href={project.url} class="card w-min variant-glass-surface card-hover overflow-hidden flex flex-col">
<header>
<img
src={project.thumbnail || fallbackThumbnail}
class="bg-black/50 aspect=[21/9] object-cover flex justify-center items-center"
alt={project.name}
loading="lazy"
on:error={handleImageError}>
</header>
<div class="p-4 space-y-4 flex-auto">
<h3 class="h3" data-toc-ignore>{project.name}</h3>
<article>
<p>
{project.description}
</p>
</article>
</div>
<hr class="opacity-50" />
<footer class="p-4 flex text-nowrap justify-start items-center space-x-4">
<Avatar src={project.author.icon || fallbackAvatar} width="w-8" />
<div class="flex-auto gap-6 flex justify-between items-center">
<h6 class="font-bold" data-toc-ignore>
By {project.author.username}
</h6>
<small>Updated {project.date}</small>
</div>
</footer>
</a>
{/each}
</div>
</div>
</div>

View File

@ -1,35 +0,0 @@
<script>
import Techs from '$lib/data/techstack.json';
import { Avatar } from '@skeletonlabs/skeleton';
import Left from './navigation-items/Left.svelte';
export let scrollInDirection;
</script>
<div id="techstack" class="w-screen h-max min-h-screen flex flex-row justify-between items-center">
<div>
<Left {scrollInDirection} />
</div>
<div class="max-w-2/3 flex flex-col justify-center items-center">
<div class="header-wrapper text-center overflow-visible">
<h2 class="flex-shrink-0 flex-grow-1 h2 font-bold pb-0 w-full overflow-hidden">My Current Tech Stack</h2>
<span class="text-sm pt-0 text-surface-700 dark:text-surface-200">Subject to Change</span>
</div>
<div class="pt-4 w-max max-w-full h-max flex flex-wrap gap-4 justify-center overflow-visible">
{#each Techs as tech}
<a href="{tech.url}" class="min-w-72 flex flex-row justify-between items-center card card-hover p-4 dark:hover:bg-surface-700 hover:bg-surface-200 rounded-lg">
<Avatar class="p-2" src={tech.logo} alt={`Logo of ${tech.name}`} />
<span class="text-xl">{tech.name}</span>
<div class="spacer"></div>
</a>
{/each}
</div>
</div>
<div class="spacer"></div>
</div>
<style>
.max-w-2\/3 {
max-width: 66.6%;
}
</style>

View File

@ -0,0 +1,27 @@
<script>
import BrandIcon from "$lib/components/items/BrandIcon.svelte";
export let brands;
</script>
<header class="w-full h-screen flex flex-col justify-center items-center">
<div id="header-wrapper" class="header-wrapper text-center">
<h1 id="title" class="h1">Alexander Harding</h1>
<h4 class="h4 m-0">Software and Application Developer</h4>
<div class="cards flex w-full justify-between mt-6">
{#each brands as brand}
<BrandIcon url={brand.url} icon={brand.icon} />
{/each}
</div>
</div>
</header>
<style>
.header-wrapper {
position: relative;
}
#title.fixed {
transform: translateY(-100%) translateX(-0.75em);
}
</style>

View File

@ -1,14 +1,11 @@
<script>
import MaterialSymbolsPerson from '~icons/material-symbols/person';
import MaterialSymbolsMail from '~icons/material-symbols/mail';
import Right from './navigation-items/Right.svelte';
export let scrollInDirection;
</script>
<div id="contact" class="w-screen h-screen flex justify-between items-center">
<div class="spacer"></div>
<form action="?/contact" class="w-full min-h-full dark:bg-surface-900 bg-surface-200 lg:w-1/2 lg:min-h-max p-8 rounded-md pr-12 lg:pr-8">
<div class="flex justify-center items-center">
<form action="?/contact" class="w-full min-h-full dark:bg-surface-700 bg-surface-200 p-8 m-4 rounded-lg pr-12">
<h1 class="h1 font-bold ps-4 pb-4">Contact Us</h1>
<hr class="opacity-25" />
<p class="pt-4">Fullname</p>
@ -38,7 +35,4 @@
<button type="submit" class="btn variant-filled-primary">Submit</button>
</div>
</form>
<div>
<Right {scrollInDirection} />
</div>
</div>

View File

@ -0,0 +1,19 @@
<script>
import LargeCard from "../items/LargeCard.svelte";
import projects from "$lib/data/projects.json";
import LargeCardSm from "../items/LargeCard-sm.svelte";
export let data;
const { avatars } = data;
</script>
<div id="projects" class="h-full">
<h2 class="h2 mb-4">Projects</h2>
<div class="h-fit gap-4 flex flex-col overflow-scroll">
{#each projects as project}
<LargeCardSm data={project} {avatars} />
{/each}
</div>
</div>

View File

@ -0,0 +1,12 @@
<script>
import SmallCard from "../items/SmallCard.svelte";
export let techs;
</script>
<h2 class="h2">My Tech Stack</h2>
<div class="cards">
{#each techs as tech}
<SmallCard data={tech} />
{/each}
</div>

View File

@ -0,0 +1,7 @@
<script>
export let url, icon, redirect;
</script>
<a href={url} target={ redirect = true ? "_blank" : "" } class="btn variant-filled-surface text-2xl p-3 rounded-md spin">
<span class="block spinner duration-500"><svelte:component this={icon}></svelte:component></span>
</a>

View File

@ -0,0 +1,39 @@
<script>
import { Avatar } from "@skeletonlabs/skeleton";
const fallbackThumbnail = "/Image_not_available.png";
const fallbackAvatar = "/default.png";
function handleImageError(event) {
event.target.src = fallbackThumbnail;
}
export let data, avatars;
const avatar = avatars[data.author.username];
</script>
<a href={data.url} class="card variant-glass-surface card-hover overflow-hidden flex flex-col w-64">
<img
src={data.thumbnail || fallbackThumbnail}
class="bg-black/50 aspect-[21/9] object-cover"
alt={data.name}
loading="lazy"
on:error={handleImageError}
/>
<div class="p-3 space-y-2 flex-auto">
<h3 class="text-lg font-bold truncate">{data.name}</h3>
<p class="text-sm line-clamp-2">{data.description}</p>
</div>
<hr class="opacity-50 mx-3" />
<footer class="p-3 flex items-center space-x-3 text-xs">
<Avatar src={avatar || fallbackAvatar} width="w-6" />
<div class="flex-auto truncate">
<h6 class="font-semibold">By {data.author.username}</h6>
<small class="opacity-75">Updated {data.date}</small>
</div>
</footer>
</a>

View File

@ -0,0 +1,43 @@
<script>
import { Avatar } from "@skeletonlabs/skeleton";
const fallbackThumbnail = "/Image_not_available.png";
const fallbackAvatar = "/default.png";
function handleImageError(event) {
event.target.src = fallbackThumbnail;
}
export let data, avatars;
const avatar = avatars[data.author.username];
</script>
<a href={data.url} class="card w-min variant-glass-surface card-hover overflow-hidden flex flex-col">
<header>
<img
src={data.thumbnail || fallbackThumbnail}
class="bg-black/50 aspect=[21/9] object-cover flex justify-center items-center"
alt={data.name}
loading="lazy"
on:error={handleImageError}>
</header>
<div class="p-4 space-y-4 flex-auto">
<h3 class="h3" data-toc-ignore>{data.name}</h3>
<article>
<p>
{data.description}
</p>
</article>
</div>
<hr class="opacity-50" />
<footer class="p-4 flex text-nowrap justify-start items-center space-x-4">
<Avatar src={avatar || fallbackAvatar} width="w-8" />
<div class="flex-auto gap-6 flex justify-between items-center">
<h6 class="font-bold" data-toc-ignore>
By {data.author.username}
</h6>
<small>Updated {data.date}</small>
</div>
</footer>
</a>

View File

@ -0,0 +1,11 @@
<script>
import { Avatar } from "@skeletonlabs/skeleton";
export let data;
</script>
<a href="{data.url}" class="m-4 flex flex-row justify-between items-center card card-hover p-4 dark:hover:bg-surface-700 hover:bg-surface-200 rounded-lg">
<Avatar class="p-2" src={data.logo} alt={`Logo of ${data.name}`} />
<span class="text-xl">{data.name}</span>
<div class="spacer"></div>
</a>

View File

@ -1,13 +0,0 @@
<script>
import ArrowDown from "~icons/material-symbols/keyboard-double-arrow-down-rounded";
export let scrollInDirection;
</script>
<div class="w-full h-full flex justify-center items-end text-3xl">
<button class="relative" on:click={() => { scrollInDirection([1, 0]) }}>
<ArrowDown />
<div class="absolute -z-10 -top-0.5 left-0 text-surface-400">
<ArrowDown />
</div>
</button>
</div>

View File

@ -1,14 +0,0 @@
<script>
import ArrowLeft from "~icons/material-symbols/keyboard-double-arrow-left-rounded";
export let scrollInDirection;
</script>
<div class="w-full h-full flex justify-start items-center text-3xl">
<button class="relative" on:click={() => { scrollInDirection([0, -1]) }}>
<ArrowLeft />
<div class="absolute -z-10 top-0 left-0.5 text-surface-400">
<ArrowLeft />
</div>
</button>
</div>

View File

@ -1,13 +0,0 @@
<script>
import ArrowRight from "~icons/material-symbols/keyboard-double-arrow-right-rounded";
export let scrollInDirection;
</script>
<div class="w-full h-full flex justify-end items-center text-3xl">
<button class="relative" on:click={() => { scrollInDirection([0, 1]) }}>
<ArrowRight />
<div class="absolute -z-10 top-0 -left-0.5 text-surface-400">
<ArrowRight />
</div>
</button>
</div>

View File

@ -1,13 +0,0 @@
<script>
import ArrowUp from "~icons/material-symbols/keyboard-double-arrow-up-rounded";
export let scrollInDirection;
</script>
<div class="w-full h-full flex justify-center items-start text-3xl">
<button class="relative" on:click={() => { scrollInDirection([-1, 0]) }}>
<ArrowUp />
<div class="absolute -z-10 top-0.5 left-0 text-surface-400">
<ArrowUp />
</div>
</button>
</div>

View File

@ -0,0 +1,117 @@
<script>
import MdiGithub from "~icons/mdi/github";
import MdiYoutube from "~icons/mdi/youtube";
import MdiDiscord from "~icons/mdi/discord";
import MdiLinkedin from "~icons/mdi/linkedin";
import MdiResume from '~icons/mdi/resume';
import CibGitea from "~icons/cib/gitea";
import MaterialSymbolsPerson from '~icons/material-symbols/person';
import MaterialSymbolsMail from '~icons/material-symbols/mail';
import MaterialSymbolsCalendarClockOutline from '~icons/material-symbols/calendar-clock-outline';
import Header from "$lib/components/cards/Header.svelte";
import SmallCard from "../items/SmallCard.svelte";
import techs from "$lib/data/techstack.json";
import projects from "$lib/data/projects.json";
import { onMount } from "svelte";
import DylanIpsum from "dylan-ipsum";
import LargeCard from "../items/LargeCard.svelte";
import Contact from "../containers/Contact.svelte";
export let data;
const brands = [
{ url: "https://www.github.com/BackwardsUser", icon: MdiGithub },
{ url: "https://git.backwards.dev/", icon: CibGitea },
{ url: "https://api.backwards.dev/files/Alexander_Harding_Resume.docx", icon: MdiResume },
{ url: "https://www.linkedin.com/in/alexander-harding-71b661265/", icon: MdiLinkedin },
{ url: "https://doodle.com/bp/backwards_user__/schedule-a-meeting", icon: MaterialSymbolsCalendarClockOutline },
{ url: "https://discord.gg/Zhq9yjhHKr", icon: MdiDiscord },
{ url: "https://www.youtube.com/@BackwardsDevelopment", icon: MdiYoutube },
];
const downloads = [];
let End = 300;
let rightStart = 0;
function setSidebars() {
let rPos = Math.min(window.scrollY / End, 1);
let lPos = Math.min(Math.max((window.scrollY - rightStart) / End, 0), 1);
document.getElementById("left").style =
`transform: translateX(${(lPos - 1) * 100}%);`;
document.getElementById("right").style =
`transform: translateX(${Math.abs(rPos - 1) * 100}%);`;
}
const avatars = data.avatars;
onMount(() => {
setSidebars();
window.addEventListener("scroll", setSidebars);
});
</script>
<div id="main" class="w-screen grid">
<div id="left" class="sidebar h-screen bg-surface-200 dark:bg-surface-500 text-center">
<h4 class="h4">My Tech Stack</h4>
<div class="cards">
{#each techs as tech}
<SmallCard data={tech} />
{/each}
</div>
</div>
<div id="content">
<Header {brands}></Header>
<div id="projects" class="flex py-8 m-4 flex-col justify-center items-center bg-surface-200 dark:bg-surface-500 rounded-lg">
<h1 class="h1 pb-8">Projects</h1>
<div class="flex flex-shrink-0 flex-wrap justify-center gap-4">
{#each projects as project}
<LargeCard data={project} {avatars} />
{/each}
</div>
</div>
<div id="contact">
<Contact />
</div>
</div>
<div id="right" class="sidebar h-screen bg-surface-200 dark:bg-surface-500 text-center">
<h4 class="h4">Downloads</h4>
{#if downloads.length > 0}
{#each downloads as download}
<p class="pt-4">-- Not Implemented --</p>
{/each}
{:else}
<p class="pt-4">-- No files uploaded --</p>
{/if}
</div>
</div>
<style>
#main {
contain: paint;
grid-template-columns: 20% 60% 20%;
position: relative;
}
#left {
border-radius: 0 0.5em 0.5em 0;
transform: translateX(-100%);
}
#right {
border-radius: 0.5em 0 0 0.5em;
transform: translateX(100%);
}
.sidebar {
position: sticky;
top: 0;
right: 0;
}
</style>

View File

@ -0,0 +1,51 @@
<script>
import MdiGithub from "~icons/mdi/github";
import MdiYoutube from "~icons/mdi/youtube";
import MdiDiscord from "~icons/mdi/discord";
import MdiLinkedin from "~icons/mdi/linkedin";
import MdiResume from '~icons/mdi/resume';
import CibGitea from "~icons/cib/gitea";
import MaterialSymbolsPerson from '~icons/material-symbols/person';
import MaterialSymbolsMail from '~icons/material-symbols/mail';
import MaterialSymbolsCalendarClockOutline from '~icons/material-symbols/calendar-clock-outline';
import Header from "../cards/Header.svelte";
import Projects from "../containers/Projects.svelte";
import TechStack from "../containers/TechStack.svelte";
import techs from "$lib/data/techstack.json";
export let data;
const brands = [
{ url: "https://www.github.com/BackwardsUser", icon: MdiGithub, redirect: false },
{ url: "https://git.backwards.dev/", icon: CibGitea, redirect: false },
{ url: "https://api.backwards.dev/files/Alexander_Harding_Resume.docx", icon: MdiResume, redirect: false },
{ url: "https://www.linkedin.com/in/alexander-harding-71b661265/", icon: MdiLinkedin, redirect: false },
{ url: "https://doodle.com/bp/backwards_user__/schedule-a-meeting", icon: MaterialSymbolsCalendarClockOutline, redirect: true },
{ url: "https://discord.gg/Zhq9yjhHKr", icon: MdiDiscord, redirect: false },
{ url: "https://www.youtube.com/@BackwardsDevelopment", icon: MdiYoutube, redirect: false },
];
</script>
<Header {brands} />
<!-- <div class="main flex flex-col gap-8">
<div class="section">
<Header {brands} />
</div>
<div class="section bg-surface-800 py-4 rounded-2xl flex flex-col">
<TechStack {techs} />
</div>
<div class="section bg-surface-800 py-4 rounded-2xl">
<Projects {data} />
</div>
</div> -->
<style>
.section {
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@ -0,0 +1,25 @@
import axios from "axios"
import { DISCORD_TOKEN } from "$env/static/private"
export const load = async () => {
let content;
try {
content = await axios.get("https://discord.com/api/v9/users/@me", {
headers: {
"Authorization": DISCORD_TOKEN
}
});
} catch {
return {
avatars: {
"BackwardsUser": "/default.png"
}
}
}
return {
avatars: {
"BackwardsUser": `https://cdn.discordapp.com/avatars/${content.data.id}/${content.data.avatar}.png?size=64`
}
}
}

View File

@ -1,143 +1,28 @@
<script>
import { onMount } from "svelte";
import Header from "$lib/components/Header.svelte";
import { fly } from "svelte/transition";
import Media from "$lib/components/Media.svelte";
import Contact from "$lib/components/Contact.svelte";
import TechStack from "$lib/components/TechStack.svelte";
import Projects from "$lib/components/Projects.svelte";
import { tableMapperValues } from "@skeletonlabs/skeleton";
import Large from "$lib/components/pages/Large.svelte";
import Small from "$lib/components/pages/Small.svelte";
function scrollToSection(section, smooth) {
section.scrollIntoView({
behavior: smooth ? "smooth" : "instant",
block: "center",
});
}
export let data;
// by ID
let layout = [
[null, "media", null],
["contact", "header", "techstack"],
[null, "projects", null],
];
let selectedId = "header";
function getIndexPathOfSelected() {
for (let rowIndex = 0; rowIndex < layout.length; rowIndex++) {
const colIndex = layout[rowIndex].indexOf(selectedId);
if (colIndex !== -1) {
return [rowIndex, colIndex];
}
}
return null;
}
function evaluateArrays(input, equationArr) {
let output = [];
if (input.length !== equationArr.length)
return null;
for (let index = 0; index < input.length; index++) {
output.push(input[index] + equationArr[index]);
}
return output;
}
function scrollInDirection(direction) {
const currentCoords = getIndexPathOfSelected();
const newCoords = evaluateArrays(currentCoords, direction);
const itemAtCoords = layout[newCoords[0]][newCoords[1]];
if (itemAtCoords == null)
return;
const sectionId = layout[newCoords[0]][newCoords[1]];
selectedId = sectionId;
scrollToSection(document.getElementById(sectionId), true);
}
let keys = {
"up": [
"ArrowUp",
"w"
],
"down": [
"ArrowDown",
"s"
],
"left": [
"ArrowLeft",
"a"
],
"right": [
"ArrowRight",
"d"
]
};
let width = 0;
onMount(() => {
scrollToSection(document.getElementById("header"));
const updateWidth = () => {
width = window.innerWidth;
};
window.addEventListener("resize", e => {
console.log(selectedId)
scrollToSection(document.getElementById(selectedId));
});
updateWidth(); // Set initial width
window.addEventListener("resize", updateWidth);
document.addEventListener("keydown", e => {
const directionArr = Object.entries(keys).filter(([_, values]) => values.some(v => v == e.key));
if (!directionArr || directionArr.length == 0)
return;
const direction = directionArr[0][0];
switch (direction) {
case "up":
scrollInDirection([-1, 0]);
break;
case "down":
scrollInDirection([1, 0]);
break;
case "left":
scrollInDirection([0, -1]);
break;
case "right":
scrollInDirection([0, 1]);
break;
}
});
return () => window.removeEventListener("resize", updateWidth);
});
</script>
<main class="grid">
<div class="page-card spacer"></div>
<Media {scrollInDirection}></Media>
<div class="page-card spacer"></div>
<Contact {scrollInDirection}></Contact>
<Header {scrollInDirection}></Header>
<TechStack {scrollInDirection}></TechStack>
<div class="page-card spacer"></div>
<Projects {scrollInDirection}></Projects>
</main>
<style>
.page-card {
width: 100vw;
height: 100vh;
/* scroll-snap-align: center; */
}
main {
grid-template-areas:
". 1 ."
"2 3 4"
". 5 .";
height: 100vh;
/* overflow-y: scroll;
scroll-snap-type: y mandatory;
scroll-behavior: smooth; */
}
main > * {
scroll-snap-align: center;
}
</style>
<div id="page-wrapper">
{#if width >= 768}
<Large {data} />
{:else}
<Small {data} />
{/if}
</div>

BIN
static/default.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -9,5 +9,8 @@ export default defineConfig({
compiler: 'svelte',
autoInstall: true
})
]
],
build: {
outDir: '../../build'
}
});