feat: index page and general structure

the index page, footer and navigation and other things have been made initially, first version
pull/2/head
Midnight 2 years ago
parent c8b478408f
commit a0da2ed3b2

@ -0,0 +1 @@
VITE_APP_URL=

1773
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -7,9 +7,12 @@
"preview": "svelte-kit preview"
},
"devDependencies": {
"@sveltejs/adapter-auto": "next",
"@sveltejs/adapter-auto": "^1.0.0-next.17",
"@sveltejs/kit": "next",
"svelte": "^3.44.0"
"autoprefixer": "^10.4.2",
"postcss": "^8.4.5",
"svelte": "^3.44.0",
"tailwindcss": "^3.0.17"
},
"type": "module",
"dependencies": {

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

@ -1,47 +1,11 @@
@import '@fontsource/fira-mono';
@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,900;1,100;1,500&display=swap");
:root {
font-family: Arial, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
--font-mono: 'Fira Mono', monospace;
--pure-white: #ffffff;
--primary-color: #b9c6d2;
--secondary-color: #d0dde9;
--tertiary-color: #edf0f8;
--accent-color: #ff3e00;
--heading-color: rgba(0, 0, 0, 0.7);
--text-color: #444444;
--background-without-opacity: rgba(255, 255, 255, 0.7);
--column-width: 42rem;
--column-margin-top: 4rem;
}
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
min-height: 100vh;
margin: 0;
background-color: var(--primary-color);
background: linear-gradient(
180deg,
var(--primary-color) 0%,
var(--secondary-color) 10.45%,
var(--tertiary-color) 41.35%
);
}
body::before {
content: '';
width: 80vw;
height: 100vh;
position: absolute;
top: 0;
left: 10vw;
z-index: -1;
background: radial-gradient(
50% 50% at 50% 50%,
var(--pure-white) 0%,
rgba(255, 255, 255, 0) 100%
);
opacity: 0.05;
:root {
font-family: "Roboto", sans-serif;
}
#svelte {
@ -49,59 +13,3 @@ body::before {
display: flex;
flex-direction: column;
}
h1,
h2,
p {
font-weight: 400;
color: var(--heading-color);
}
p {
line-height: 1.5;
}
a {
color: var(--accent-color);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
h1 {
font-size: 2rem;
text-align: center;
}
h2 {
font-size: 1rem;
}
pre {
font-size: 16px;
font-family: var(--font-mono);
background-color: rgba(255, 255, 255, 0.45);
border-radius: 3px;
box-shadow: 2px 2px 6px rgb(255 255 255 / 25%);
padding: 0.5em;
overflow-x: auto;
color: var(--text-color);
}
input,
button {
font-size: inherit;
font-family: inherit;
}
button:focus:not(:focus-visible) {
outline: none;
}
@media (min-width: 720px) {
h1 {
font-size: 2.4rem;
}
}

@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
%svelte.head%
</head>
<body>
<div id="svelte">%svelte.body%</div>
<body id="svelte">
%svelte.body%
</body>
</html>

3
src/global.d.ts vendored

@ -1 +1,4 @@
/// <reference types="@sveltejs/kit" />
interface ImportMetaEnv {
VITE_APP_URL: string;
}

@ -0,0 +1,53 @@
<script>
import { onMount } from "svelte";
import { getCookie } from "./helpers/cookies";
function close() {
document.cookie = "closedConstruction=true";
let classes = document.querySelector("#item").classList;
classes.add("hidden");
}
function shouldShow() {
let cookie = getCookie("closedConstruction");
let classes = document.querySelector("#item").classList;
console.log(cookie);
if (cookie != "true") {
classes.add("block");
classes.remove("hidden");
} else {
classes.add("hidden");
classes.adremoved("block");
}
}
onMount(() => {
shouldShow();
});
</script>
<section
class="relative py-2 text-center bg-amber-500 text-white hidden"
id="item"
>
<div
class="container flex lg:flex-row flex-col justify-between items-center"
>
<h2 class="text-xl font-bold text-left">🚧 Under Construction 🏗️</h2>
<p class="lg:text-right text-sm px-2 lg:px-0">
I started making this a few days ago after I realized I haven't had
a good personal website in a while. Please bear with me as I fill
this up and make it look better!
</p>
</div>
<div
class="absolute inset-0 z-10 h-full flex items-start lg:items-center justify-end pt-2 lg:pt-0 pr-3"
>
<button type="none" on:click={close}> </button>
</div>
</section>

@ -1,97 +0,0 @@
<script>
import { spring } from 'svelte/motion';
let count = 0;
const displayed_count = spring();
$: displayed_count.set(count);
$: offset = modulo($displayed_count, 1);
function modulo(n, m) {
// handle negative numbers
return ((n % m) + m) % m;
}
</script>
<div class="counter">
<button on:click={() => (count -= 1)} aria-label="Decrease the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5" />
</svg>
</button>
<div class="counter-viewport">
<div class="counter-digits" style="transform: translate(0, {100 * offset}%)">
<strong style="top: -100%" aria-hidden="true">{Math.floor($displayed_count + 1)}</strong>
<strong>{Math.floor($displayed_count)}</strong>
</div>
</div>
<button on:click={() => (count += 1)} aria-label="Increase the counter by one">
<svg aria-hidden="true" viewBox="0 0 1 1">
<path d="M0,0.5 L1,0.5 M0.5,0 L0.5,1" />
</svg>
</button>
</div>
<style>
.counter {
display: flex;
border-top: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin: 1rem 0;
}
.counter button {
width: 2em;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
border: 0;
background-color: transparent;
color: var(--text-color);
font-size: 2rem;
}
.counter button:hover {
background-color: var(--secondary-color);
}
svg {
width: 25%;
height: 25%;
}
path {
vector-effect: non-scaling-stroke;
stroke-width: 2px;
stroke: var(--text-color);
}
.counter-viewport {
width: 8em;
height: 4em;
overflow: hidden;
text-align: center;
position: relative;
}
.counter-viewport strong {
position: absolute;
display: flex;
width: 100%;
height: 100%;
font-weight: 400;
color: var(--accent-color);
font-size: 4rem;
align-items: center;
justify-content: center;
}
.counter-digits {
position: absolute;
width: 100%;
height: 100%;
}
</style>

@ -0,0 +1,30 @@
<script>
import Social from "./footer/Social.svelte";
</script>
<span class="flex gap-10">
<Social
url="https://twitter.com/midblep"
title="The Bird App"
icon="🐦"
text="Twitter"
/>
<Social
url="https://gitlab.com/midblep"
title="I don't use GitHub"
icon="🦊"
text="GitLab"
/>
<Social
url="https://discord.com/users/191525900880183296"
title="Mid#0001"
icon="💬"
text="Discord"
/>
<Social
url="mailto:mrrmiddynight@gmail.com"
title="mrrmiddynight@gmail.com"
icon="✉️"
text="E-Mail"
/>
</span>

@ -0,0 +1,42 @@
<script>
import { onMount } from "svelte";
import { getCookie } from "./helpers/cookies";
let classes =
"w-full h-full py-1 px-5 flex justify-center items-center hover:bg-orange-200 dark:hover:bg-orange-700 duration-300";
function switchTheme() {
if (document.documentElement.classList.contains("dark")) {
setLight();
} else {
setDark();
}
}
function setLight() {
document.documentElement.classList.remove("dark");
document.querySelector("#theme").innerHTML = "🌙";
document.cookie = "theme=light";
}
function setDark() {
document.documentElement.classList.add("dark");
document.querySelector("#theme").innerHTML = "☀️";
document.cookie = "theme=dark";
}
onMount(() => {
let theme = getCookie("theme");
if (!theme) {
document.cookie = "theme=light";
} else {
if (theme == "light") setLight();
else if (theme == "dark") setDark();
}
});
</script>
<button type="none" class={classes} on:click={switchTheme} id="theme">
☀️ <span class="text-xs text-gray-400 px-2">/</span> 🌙
</button>

@ -0,0 +1,10 @@
<script>
export let href;
</script>
<a
{href}
class="py-2 px-5 bg-white dark:bg-black bg-opacity-70 dark:bg-opacity-50 rounded-lg hover:bg-opacity-100 dark:hover:bg-opacity-100 duration-150 text-gray-800 hover:text-black dark:text-gray-200 dark:hover:text-white"
>
<slot />
</a>

@ -0,0 +1,29 @@
<script>
import Socials from "$lib/Socials.svelte";
import ThemeSwitcher from "$lib/ThemeSwitcher.svelte";
import Social from "./Social.svelte";
</script>
<main
class="py-10 px-2 lg:px-10 bg-white dark:bg-black dark:bg-opacity-50 text-black dark:text-white shadow"
>
<section
class="container flex flex-col-reverse lg:flex-row justify-between gap-10"
>
<div class="flex flex-col gap-3">
<h4 class="font-sm font-bold">Bart Industries</h4>
<hr />
<p class="text-xs">Copyright 2022 Mid</p>
</div>
<div class="flex flex-col gap-5 lg:items-end">
<Socials />
<div class="text-amber-500 flex gap-2 items-center font-bold">
<span class="text-xs">Theme:</span>
<span class="bg-gray-100 dark:bg-gray-800"
><ThemeSwitcher /></span
>
</div>
</div>
</section>
</main>

@ -0,0 +1,17 @@
<script>
export let text;
export let icon;
export let url;
export let title;
if (title == null) title = text;
</script>
<a
href={url}
{title}
class="text-xs flex flex-col justify-center hover:scale-110 duration-150"
>
<span class="text-4xl ">{icon}</span>
<span class="text-gray-600 dark:text-gray-300 font-light">{text}</span>
</a>

@ -1,49 +0,0 @@
// this action (https://svelte.dev/tutorial/actions) allows us to
// progressively enhance a <form> that already works without JS
export function enhance(form, { pending, error, result }) {
let current_token;
async function handle_submit(e) {
const token = (current_token = {});
e.preventDefault();
const body = new FormData(form);
if (pending) pending(body, form);
try {
const res = await fetch(form.action, {
method: form.method,
headers: {
accept: 'application/json'
},
body
});
if (token !== current_token) return;
if (res.ok) {
result(res, form);
} else if (error) {
error(res, null, form);
} else {
console.error(await res.text());
}
} catch (e) {
if (error) {
error(null, e, form);
} else {
throw e;
}
}
}
form.addEventListener('submit', handle_submit);
return {
destroy() {
form.removeEventListener('submit', handle_submit);
}
};
}

@ -0,0 +1,29 @@
<script>
export let text;
export let url;
export let type;
export let disabled;
let classes = "h-full py-2 px-5 flex justify-center items-center lowercase";
switch (type) {
case "title":
classes +=
" text-xl font-bold text-white bg-amber-500 duration-300";
break;
default:
classes +=
" hover:bg-orange-200 dark:hover:bg-orange-700 duration-300";
break;
}
if (disabled) {
url = null;
classes += " cursor-not-allowed";
}
</script>
<a href={url} class={classes}>
{text}
</a>

@ -0,0 +1,41 @@
<script>
import MenuIcon from "./MenuIcon.svelte";
export let text;
export let type;
let classes = "h-full px-5 flex justify-center items-center";
switch (type) {
case "title":
classes +=
" text-xl font-bold text-white bg-amber-500 duration-300";
break;
default:
classes +=
" hover:bg-orange-200 dark:hover:bg-orange-700 duration-300";
break;
}
function openMenu() {
let dropdown = document.querySelector("#dropdown");
console.log(dropdown);
dropdown.classList.toggle("block");
dropdown.classList.toggle("hidden");
}
</script>
<main class="relative">
<!-- <MenuIcon /> -->
<button on:click={openMenu} type="none" class={classes} id="trigger">
{text}
</button>
<div
class="absolute w-screen right-0 bg-white dark:bg-black hidden z-50"
id="dropdown"
>
<slot />
</div>
</main>

@ -0,0 +1,23 @@
<script>
export let text;
export let url;
export let type;
let classes = "h-full px-5 flex justify-center items-center";
switch (type) {
case "title":
classes +=
" text-xl font-bold text-white bg-amber-500 duration-300";
break;
default:
classes +=
" hover:bg-orange-200 dark:hover:bg-orange-700 duration-300";
break;
}
</script>
<a href={url} class={classes}>
{text}
</a>

@ -1,124 +1,11 @@
<script>
import { page } from '$app/stores';
import logo from './svelte-logo.svg';
import Nav from "./Nav.svelte";
</script>
<header>
<div class="corner">
<a href="https://kit.svelte.dev">
<img src={logo} alt="SvelteKit" />
</a>
</div>
<nav>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
</svg>
<ul>
<li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li>
<li class:active={$page.url.pathname === '/about'}>
<a sveltekit:prefetch href="/about">About</a>
</li>
<li class:active={$page.url.pathname === '/todos'}>
<a sveltekit:prefetch href="/todos">Todos</a>
</li>
</ul>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
</svg>
</nav>
<div class="corner">
<!-- TODO put something else here? github link? -->
</div>
</header>
<style>
header {
display: flex;
justify-content: space-between;
}
.corner {
width: 3em;
height: 3em;
}
.corner a {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.corner img {
width: 2em;
height: 2em;
object-fit: contain;
}
nav {
display: flex;
justify-content: center;
--background: rgba(255, 255, 255, 0.7);
}
svg {
width: 2em;
height: 3em;
display: block;
}
path {
fill: var(--background);
}
ul {
position: relative;
padding: 0;
margin: 0;
height: 3em;
display: flex;
justify-content: center;
align-items: center;
list-style: none;
background: var(--background);
background-size: contain;
}
li {
position: relative;
height: 100%;
}
li.active::before {
--size: 6px;
content: '';
width: 0;
height: 0;
position: absolute;
top: 0;
left: calc(50% - var(--size));
border: var(--size) solid transparent;
border-top: var(--size) solid var(--accent-color);
}
nav a {
display: flex;
height: 100%;
align-items: center;
padding: 0 1em;
color: var(--heading-color);
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.1em;
text-decoration: none;
transition: color 0.2s linear;
}
a:hover {
color: var(--accent-color);
}
</style>
<main
class="bg-white dark:bg-black dark:bg-opacity-50 text-black dark:text-white shadow z-50"
>
<section class="container">
<Nav />
</section>
</main>

@ -0,0 +1,45 @@
<script>
function myFunction(x) {
document.querySelector("#icon").classList.toggle("change");
}
</script>
<div class="container" id="icon" on:click={() => myFunction(this)}>
<div class="bar1" />
<div class="bar2" />
<div class="bar3" />
</div>
<style>
.container {
display: inline-block;
cursor: pointer;
}
.bar1,
.bar2,
.bar3 {
width: 35px;
height: 5px;
background-color: #333;
margin: 6px 0;
transition: 0.4s;
}
/* Rotate first bar */
.change .bar1 {
-webkit-transform: rotate(-45deg) translate(-9px, 6px);
transform: rotate(-45deg) translate(-9px, 6px);
}
/* Fade out the second bar */
.change .bar2 {
opacity: 0;
}
/* Rotate last bar */
.change .bar3 {
-webkit-transform: rotate(45deg) translate(-8px, -8px);
transform: rotate(45deg) translate(-8px, -8px);
}
</style>

@ -0,0 +1,36 @@
<script>
import ThemeSwitcher from "$lib/ThemeSwitcher.svelte";
import Button from "./Button.svelte";
import Dropdown from "./Dropdown.svelte";
import DropdownButton from "./DropdownButton.svelte";
</script>
<div class="flex justify-between items-center">
<a class="flex h-14" href="/">
<!-- <Button url="/" text="<img src='/bart.png' />" type="title" /> -->
<img src="/bart.png" alt="Logo" class="h-full px-5 py-2 bg-amber-500" />
</a>
<div class="hidden lg:flex h-14">
<Button url="/" text="🏠 Home" />
<Button disabled="true" url="/esu" text="📒 ESU" />
<Button disabled="true" url="/portfolio" text="🧰 works" />
<Button disabled="true" url="/furry" text="🐈‍⬛ Furry" />
<Button disabled="true" url="http://ad.localhost" text="🔞 AD Site" />
<span><ThemeSwitcher /></span>
</div>
<div class="flex lg:hidden h-14">
<Dropdown text="🍔">
<Button url="/" text="🏠 Home" />
<Button disabled="true" url="/esu" text="📒 ESU" />
<Button disabled="true" url="/portfolio" text="🧰 works" />
<Button disabled="true" url="/furry" text="🐈‍⬛ Furry" />
<Button
disabled="true"
url="http://ad.localhost"
text="🔞 AD Site"
/>
<ThemeSwitcher />
</Dropdown>
</div>
</div>

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.1566,22.8189c-10.4-14.8851-30.94-19.2971-45.7914-9.8348L22.2825,29.6078A29.9234,29.9234,0,0,0,8.7639,49.6506a31.5136,31.5136,0,0,0,3.1076,20.2318A30.0061,30.0061,0,0,0,7.3953,81.0653a31.8886,31.8886,0,0,0,5.4473,24.1157c10.4022,14.8865,30.9423,19.2966,45.7914,9.8348L84.7167,98.3921A29.9177,29.9177,0,0,0,98.2353,78.3493,31.5263,31.5263,0,0,0,95.13,58.117a30,30,0,0,0,4.4743-11.1824,31.88,31.88,0,0,0-5.4473-24.1157" style="fill:#ff3e00"/><path d="M45.8171,106.5815A20.7182,20.7182,0,0,1,23.58,98.3389a19.1739,19.1739,0,0,1-3.2766-14.5025,18.1886,18.1886,0,0,1,.6233-2.4357l.4912-1.4978,1.3363.9815a33.6443,33.6443,0,0,0,10.203,5.0978l.9694.2941-.0893.9675a5.8474,5.8474,0,0,0,1.052,3.8781,6.2389,6.2389,0,0,0,6.6952,2.485,5.7449,5.7449,0,0,0,1.6021-.7041L69.27,76.281a5.4306,5.4306,0,0,0,2.4506-3.631,5.7948,5.7948,0,0,0-.9875-4.3712,6.2436,6.2436,0,0,0-6.6978-2.4864,5.7427,5.7427,0,0,0-1.6.7036l-9.9532,6.3449a19.0329,19.0329,0,0,1-5.2965,2.3259,20.7181,20.7181,0,0,1-22.2368-8.2427,19.1725,19.1725,0,0,1-3.2766-14.5024,17.9885,17.9885,0,0,1,8.13-12.0513L55.8833,23.7472a19.0038,19.0038,0,0,1,5.3-2.3287A20.7182,20.7182,0,0,1,83.42,29.6611a19.1739,19.1739,0,0,1,3.2766,14.5025,18.4,18.4,0,0,1-.6233,2.4357l-.4912,1.4978-1.3356-.98a33.6175,33.6175,0,0,0-10.2037-5.1l-.9694-.2942.0893-.9675a5.8588,5.8588,0,0,0-1.052-3.878,6.2389,6.2389,0,0,0-6.6952-2.485,5.7449,5.7449,0,0,0-1.6021.7041L37.73,51.719a5.4218,5.4218,0,0,0-2.4487,3.63,5.7862,5.7862,0,0,0,.9856,4.3717,6.2437,6.2437,0,0,0,6.6978,2.4864,5.7652,5.7652,0,0,0,1.602-.7041l9.9519-6.3425a18.978,18.978,0,0,1,5.2959-2.3278,20.7181,20.7181,0,0,1,22.2368,8.2427,19.1725,19.1725,0,0,1,3.2766,14.5024,17.9977,17.9977,0,0,1-8.13,12.0532L51.1167,104.2528a19.0038,19.0038,0,0,1-5.3,2.3287" style="fill:#fff"/></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

@ -0,0 +1,15 @@
export let getCookie = (cname) => {
let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(";");
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == " ") {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
};

@ -0,0 +1,3 @@
export const variables = {
appUrl: import.meta.env.VITE_APP_URL,
};

@ -0,0 +1,108 @@
<script>
import Socials from "$lib/Socials.svelte";
const array = [
"/banner1.png",
"/banner2.png",
"/banner3.png",
"/banner4.png",
"/banner5.jpeg",
"/banner6.png",
"/banner7.png",
"/banner8.png",
];
const randomBannerPic = array[Math.floor(Math.random() * array.length)];
</script>
<main class="flex gap-10 items-center justify-between px-6 lg:px-20">
<div class="flex flex-col gap-5 justify-center w-full">
<span>
<img src="/hello.png" alt="Hello!" class="h-20 popout" />
</span>
<span class="flex flex-wrap gap-2 items-end">
<h2 class="text-4xl font-extrabold">I'm Midnight,</h2>
<span class="text-3xl font-extralight">
i like to make things on the interwebs
</span>
</span>
<hr class="w-1/3 my-2" />
<span class="my-2">
<p class="text-gray-800 dark:text-gray-300 lg:w-3/4">
Bart Industries is a collection of my works, projects and
ambitions <br /> including my furry characters, stories and other
things i feel like sharing online.
</p>
</span>
<Socials />
</div>
<div class="hidden lg:block float h-full w-2/3">
<img
src={randomBannerPic}
alt="mid"
class="rounded-xl shadow-xl w-full"
/>
</div>
</main>
<style>
.popout {
animation: popout;
animation-duration: 1s;
animation-timing-function: ease-in-out;
}
@keyframes popout {
0% {
transform: scale(1) rotate(0deg);
}
50% {
transform: scale(1.2) rotate(-5deg);
}
100% {
transform: scale(1) rotate(0deg);
}
}
@keyframes float {
0% {
padding-top: 0px;
padding-bottom: 10px;
}
50% {
padding-top: 10px;
padding-bottom: 0px;
}
100% {
padding-top: 0px;
padding-bottom: 10px;
}
}
.float {
animation: float;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
@keyframes float {
0% {
padding-top: 0px;
padding-bottom: 10px;
}
50% {
padding-top: 10px;
padding-bottom: 0px;
}
100% {
padding-top: 0px;
padding-bottom: 10px;
}
}
</style>

@ -0,0 +1,40 @@
<script>
export let color;
export let rotation;
export let buttonText;
export let buttonHref;
let skew;
switch (rotation) {
case "left":
skew = "skew-x-1 lg:skew-x-6";
break;
case "right":
skew = "-skew-x-1 lg:-skew-x-6";
break;
}
</script>
<div class="flex flex-col gap-2 w-full relative py-8 px-8 lg:px-12 lg:py-10">
<div
class="{skew} {color} absolute inset-0 z-0 w-full shadow-xl rounded-xl flex flex-col justify-end"
>
{#if buttonText != null && buttonHref != null}
<a
href={buttonHref}
class="rounded-b-xl bg-white bg-opacity-50 p-5 hover:bg-opacity-100 duration-150 text-base lg:text-lg font-bold dark:text-black"
>
<span class="p-5 z-10">{buttonText}</span>
</a>
{/if}
</div>
<div class="z-10 flex flex-col gap-5 flex-grow">
<slot />
{#if buttonText != null && buttonHref != null}
<span class="h-16 lg:h-10" />
{/if}
</div>
</div>

@ -0,0 +1,96 @@
<script>
import Socials from "$lib/Socials.svelte";
import { variables } from "$lib/helpers/variables";
let email;
let message;
let notify;
function send() {
email = "";
message = "";
notify = "Email sent!";
}
</script>
<main class="flex flex-col gap-5">
<span class="p-5 flex justify-center">
<Socials />
</span>
<section
class="bg-gray-200 dark:bg-gray-800 p-10 rounded-xl shadow w-full flex flex-col gap-5"
>
<h3 class="text-xl font-bold">Quick compose email</h3>
<form
class="flex flex-col gap-5"
action="https://api.staticforms.xyz/submit"
method="post"
>
<input
type="hidden"
name="accessKey"
value="f237579b-85d1-414f-846c-409b8c8f57f0"
/>
<input type="text" name="honeypot" style="display: none;" />
<input type="hidden" name="redirectTo" value={variables.appUrl} />
<div class="flex flex-col">
<label for="email" value="E-Mail" class="text-lg font-bold"
>E-Mail</label
>
<input
id="email"
name="email"
type="text"
class="text-lg py-1 px-2 rounded-lg shadow"
bind:value={email}
/>
</div>
<div class="flex flex-col">
<label for="message" value="E-Mail" class="text-lg font-bold"
>Message</label
>
<textarea
id="message"
name="message"
class="text-lg py-1 px-2 rounded-lg shadow"
bind:value={message}
rows="5"
/>
</div>
<div class="flex flex-col">
<button
type="submit"
on:click={send}
class="bg-white py-2 px-3 rounded-lg font-bold shadow"
>Send</button
>
</div>
{#if notify}
<p class="text-green-500 font-bold text-lg">{notify}</p>
{/if}
</form>
</section>
</main>
<style>
button {
transition-duration: 0.3s;
}
button:hover {
transition-duration: 0.3s;
transform: translateY(-5px);
}
button:active {
transition-duration: 0.3s;
transform: translateY(5px);
}
</style>

@ -0,0 +1,3 @@
<main>
<h1>Error</h1>
</main>

@ -1,45 +1,19 @@
<script>
import Header from '$lib/header/Header.svelte';
import '../app.css';
</script>
<Header />
import Construction from "$lib/Construction.svelte";
import Footer from "$lib/footer/Footer.svelte";
import Header from "$lib/header/Header.svelte";
<main>
<slot />
</main>
<footer>
<p>visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit</p>
</footer>
import "../app.css";
</script>
<style>
main {
flex: 1;
display: flex;
flex-direction: column;
padding: 1rem;
width: 100%;
max-width: 1024px;
margin: 0 auto;
box-sizing: border-box;
}
<div class="bg-gray-100 dark:bg-gray-900 text-black dark:text-white">
<Construction />
footer {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 40px;
}
<div class="min-h-screen">
<Header />
footer a {
font-weight: bold;
}
<slot />
</div>
@media (min-width: 480px) {
footer {
padding: 40px 0;
}
}
</style>
<Footer />
</div>

@ -1,50 +0,0 @@
<script context="module">
import { browser, dev } from '$app/env';
// we don't need any JS on this page, though we'll load
// it in dev so that we get hot module replacement...
export const hydrate = dev;
// ...but if the client-side router is already loaded
// (i.e. we came here from elsewhere in the app), use it
export const router = browser;
// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in prod
export const prerender = true;
</script>
<svelte:head>
<title>About</title>
</svelte:head>
<div class="content">
<h1>About this app</h1>
<p>
This is a <a href="https://kit.svelte.dev">SvelteKit</a> app. You can make your own by typing the
following into your command line and following the prompts:
</p>
<!-- TODO lose the @next! -->
<pre>npm init svelte@next</pre>
<p>
The page you're looking at is purely static HTML, with no client-side interactivity needed.
Because of that, we don't need to load any JavaScript. Try viewing the page's source, or opening
the devtools network panel and reloading.
</p>
<p>
The <a href="/todos">TODOs</a> page illustrates SvelteKit's data loading and form handling. Try using
it with JavaScript disabled!
</p>
</div>
<style>
.content {
width: 100%;
max-width: var(--column-width);
margin: var(--column-margin-top) auto 0 auto;
}
</style>

@ -0,0 +1,9 @@
<script>
import Socials from "$lib/Socials.svelte";
</script>
<svelte:head>
<title>Home</title>
</svelte:head>
<main />

@ -0,0 +1,9 @@
<script>
import Socials from "$lib/Socials.svelte";
</script>
<svelte:head>
<title>Home</title>
</svelte:head>
<main />

@ -1,59 +1,163 @@
<script context="module">
export const prerender = true;
</script>
<script>
import Counter from '$lib/Counter.svelte';
import AnchorButton from "$lib/elements/AnchorButton.svelte";
import Banner from "$lib/home/Banner.svelte";
import Card from "$lib/home/Card.svelte";
import Form from "$lib/home/Form.svelte";
</script>
<svelte:head>
<title>Home</title>
<title>Home | Bart Industries</title>
</svelte:head>
<section>
<h1>
<div class="welcome">
<picture>
<source srcset="svelte-welcome.webp" type="image/webp" />
<img src="svelte-welcome.png" alt="Welcome" />
</picture>
<main class="relative">
<section class="container py-20">
<Banner />
</section>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320">
<path
class="block dark:hidden"
fill="rgba(229, 231, 235, 1)"
fill-opacity="1"
d="M0,64L34.3,85.3C68.6,107,137,149,206,165.3C274.3,181,343,171,411,160C480,149,549,139,617,133.3C685.7,128,754,128,823,154.7C891.4,181,960,235,1029,240C1097.1,245,1166,203,1234,160C1302.9,117,1371,75,1406,53.3L1440,32L1440,320L1405.7,320C1371.4,320,1303,320,1234,320C1165.7,320,1097,320,1029,320C960,320,891,320,823,320C754.3,320,686,320,617,320C548.6,320,480,320,411,320C342.9,320,274,320,206,320C137.1,320,69,320,34,320L0,320Z"
/>
<path
class="hidden dark:block"
fill="rgba(31, 41, 55, 1)"
fill-opacity="1"
d="M0,64L34.3,85.3C68.6,107,137,149,206,165.3C274.3,181,343,171,411,160C480,149,549,139,617,133.3C685.7,128,754,128,823,154.7C891.4,181,960,235,1029,240C1097.1,245,1166,203,1234,160C1302.9,117,1371,75,1406,53.3L1440,32L1440,320L1405.7,320C1371.4,320,1303,320,1234,320C1165.7,320,1097,320,1029,320C960,320,891,320,823,320C754.3,320,686,320,617,320C548.6,320,480,320,411,320C342.9,320,274,320,206,320C137.1,320,69,320,34,320L0,320Z"
/>
</svg>
<section class="bg-gray-200 dark:bg-gray-800 -mt-1">
<div class="container px-5 flex flex-col gap-10 justify-center">
<h2 class="text-2xl font-bold italic">Places I'm a part of</h2>
<div class="flex flex-col lg:flex-row gap-5 justify-between">
<Card
buttonHref="/works#puppypride"
buttonText="🌐 More details and pictures on my Works page"
rotation="left"
color="bg-blue-300 dark:bg-blue-700"
>
<h3
class="text-xl font-bold inline-flex items-center gap-3"
>
<img
src="/puppypride.png"
class="h-8"
alt="puppypride"
/> Puppy Pride
</h3>
<p class="text-sm text-gray-800 dark:text-gray-100">
I was the sole developer of the new Puppy Pride social
network over the course of 2021, and continuing on to
this day. The whole site was built from the ground up
using the TALL stack in PHP.
<br /><br />
The network sports a whole array of social features including
blogging, picture albums, statuses, extensive user profiles,
events, customizable groups with roles and custom pages,
instant messaging, various networking tools like friending
and blocking, notifications through channels like push and
mail, live feed, subscriptions and views, extensive comment
sections, discussions, and much more.
<br /><br />
The project is nearing completion and will be released soon.
Puppy Pride is the world's leading pup play community and
has tens of thousands of members across the globe.
</p>
</Card>
<Card
buttonHref="https://puppypride.social"
buttonText="🚪 Join the Discord"
rotation="left"
color="bg-orange-300 dark:bg-orange-800"
>
<h3
class="text-xl font-bold inline-flex items-center gap-3"
>
<img
src="/moonlitden.png"
class="h-8"
alt="moonlitden"
/> The Moonlit Den
</h3>
<p class="text-sm text-gray-800 dark:text-gray-100">
Successor to the wildly popular Wumpus' Universe, a
cornerstone of Discord's early fanatic community, The
Moonlit Den is a small piece of what it once was. Today,
it continues to do much of the same it always did,
becoming more sophisticated and professional as it's
main sponsor DubbelNull improves.
<br /><br />
Moonlit Den is a generalistic Discord community aimed at
being a friendly place to make your home online. We run various
gameservers, have game nights together, and overall just
like to chat about whatever.
</p>
</Card>
<Card
buttonHref="https://dubbelnull.com"
buttonText="➡️ Go to DubbelNull.com"
rotation="left"
color="bg-gray-300 dark:bg-gray-700"
>
<h3
class="text-xl font-bold inline-flex items-center gap-3"
>
<img
src="/dubbelnull.png"
class="h-10 -my-2"
alt="dubbelnull"
/> DubbelNull
</h3>
<p class="text-sm text-gray-800 dark:text-gray-100">
I am the founder and CTO of DubbelNull, and do most of
the programming and technological work there today. We
run our own server architecture and sponsor several
communities to do what they want, such as run their
gameservers, websites, scripts and services.
<br /><br />
DubbelNull currently offers specialized website design and
construction of any type, be it a webapp or simple portfolio.
We focus on efficiency in communication and satisfaction
of the end result.
<br /><br />
We are also looking into expanding into the SaaS business,
notably with our own Cloud Storage solution built on top
of Nextcloud, and a chatting app in the works called Flame.
</p>
</Card>
</div>
</div>
</section>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320">
<path
class="block dark:hidden"
fill="rgba(229, 231, 235, 1)"
fill-opacity="1"
d="M0,320L34.3,288C68.6,256,137,192,206,186.7C274.3,181,343,235,411,229.3C480,224,549,160,617,128C685.7,96,754,96,823,112C891.4,128,960,160,1029,170.7C1097.1,181,1166,171,1234,160C1302.9,149,1371,139,1406,133.3L1440,128L1440,0L1405.7,0C1371.4,0,1303,0,1234,0C1165.7,0,1097,0,1029,0C960,0,891,0,823,0C754.3,0,686,0,617,0C548.6,0,480,0,411,0C342.9,0,274,0,206,0C137.1,0,69,0,34,0L0,0Z"
/>
<path
class="hidden dark:block"
fill="rgba(31, 41, 55, 1)"
fill-opacity="1"
d="M0,320L34.3,288C68.6,256,137,192,206,186.7C274.3,181,343,235,411,229.3C480,224,549,160,617,128C685.7,96,754,96,823,112C891.4,128,960,160,1029,170.7C1097.1,181,1166,171,1234,160C1302.9,149,1371,139,1406,133.3L1440,128L1440,0L1405.7,0C1371.4,0,1303,0,1234,0C1165.7,0,1097,0,1029,0C960,0,891,0,823,0C754.3,0,686,0,617,0C548.6,0,480,0,411,0C342.9,0,274,0,206,0C137.1,0,69,0,34,0L0,0Z"
/>
</svg>
to your new<br />SvelteKit app
</h1>
<h2>
try editing <strong>src/routes/index.svelte</strong>
</h2>
<Counter />
</section>
<style>
section {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex: 1;
}
h1 {
width: 100%;
}
.welcome {
position: relative;
width: 100%;
height: 0;
padding: 0 0 calc(100% * 495 / 2048) 0;
}
.welcome img {
position: absolute;
width: 100%;
height: 100%;
top: 0;
display: block;
}
</style>
<section
class="container px-5 lg:px-0 pb-20 flex flex-col lg:flex-row gap-10 justify-between items-center lg:items-start"
>
<img src="/contact.png" class="lg:w-1/3 h-full" alt="contact" />
<div class="lg:w-1/3">
<Form />
</div>
</section>
</main>

@ -0,0 +1,9 @@
<script>
import Socials from "$lib/Socials.svelte";
</script>
<svelte:head>
<title>Home</title>
</svelte:head>
<main />

@ -1,14 +0,0 @@
import { api } from './_api';
// PATCH /todos/:uid.json
export const patch = async (request) => {
return api(request, `todos/${request.locals.userid}/${request.params.uid}`, {
text: request.body.get('text'),
done: request.body.has('done') ? !!request.body.get('done') : undefined
});
};
// DELETE /todos/:uid.json
export const del = async (request) => {
return api(request, `todos/${request.locals.userid}/${request.params.uid}`);
};

@ -1,45 +0,0 @@
/*
This module is used by the /todos.json and /todos/[uid].json
endpoints to make calls to api.svelte.dev, which stores todos
for each user. The leading underscore indicates that this is
a private module, _not_ an endpoint visiting /todos/_api
will net you a 404 response.
(The data on the todo app will expire periodically; no
guarantees are made. Don't use it to organise your life.)
*/
const base = 'https://api.svelte.dev';
export async function api(request, resource, data) {
// user must have a cookie set
if (!request.locals.userid) {
return { status: 401 };
}
const res = await fetch(`${base}/${resource}`, {
method: request.method,
headers: {
'content-type': 'application/json'
},
body: data && JSON.stringify(data)
});
// if the request came from a <form> submission, the browser's default
// behaviour is to show the URL corresponding to the form's "action"
// attribute. in those cases, we want to redirect them back to the
// /todos page, rather than showing the response
if (res.ok && request.method !== 'GET' && request.headers.accept !== 'application/json') {
return {
status: 303,
headers: {
location: '/todos'
}
};
}
return {
status: res.status,
body: await res.json()
};
}

@ -1,28 +0,0 @@
import { api } from './_api';
// GET /todos.json
export const get = async (request) => {
// request.locals.userid comes from src/hooks.js
const response = await api(request, `todos/${request.locals.userid}`);
if (response.status === 404) {
// user hasn't created a todo list.
// start with an empty array
return { body: [] };
}
return response;
};
// POST /todos.json
export const post = async (request) => {
const response = await api(request, `todos/${request.locals.userid}`, {
// because index.svelte posts a FormData object,
// request.body is _also_ a (readonly) FormData
// object, which allows us to get form data
// with the `body.get(key)` method
text: request.body.get('text')
});
return response;
};

@ -1,220 +0,0 @@
<script context="module">
import { enhance } from '$lib/form';
// see https://kit.svelte.dev/docs#loading
export const load = async ({ fetch }) => {
const res = await fetch('/todos.json');
if (res.ok) {
const todos = await res.json();
return {
props: { todos }
};
}
const { message } = await res.json();
return {
error: new Error(message)
};
};
</script>
<script>
import { scale } from 'svelte/transition';
import { flip } from 'svelte/animate';
export let todos;
async function patch(res) {
const todo = await res.json();
todos = todos.map((t) => {
if (t.uid === todo.uid) return todo;
return t;
});
}
</script>
<svelte:head>
<title>Todos</title>
</svelte:head>
<div class="todos">
<h1>Todos</h1>
<form
class="new"
action="/todos.json"
method="post"
use:enhance={{
result: async (res, form) => {
const created = await res.json();
todos = [...todos, created];
form.reset();
}
}}
>
<input name="text" aria-label="Add todo" placeholder="+ tap to add a todo" />
</form>
{#each todos as todo (todo.uid)}
<div
class="todo"
class:done={todo.done}
transition:scale|local={{ start: 0.7 }}
animate:flip={{ duration: 200 }}
>
<form
action="/todos/{todo.uid}.json?_method=PATCH"
method="post"
use:enhance={{
pending: (data) => {
todo.done = !!data.get('done');
},
result: patch
}}
>
<input type="hidden" name="done" value={todo.done ? '' : 'true'} />
<button class="toggle" aria-label="Mark todo as {todo.done ? 'not done' : 'done'}" />
</form>
<form
class="text"
action="/todos/{todo.uid}.json?_method=PATCH"
method="post"
use:enhance={{
result: patch
}}
>
<input aria-label="Edit todo" type="text" name="text" value={todo.text} />
<button class="save" aria-label="Save todo" />
</form>
<form
action="/todos/{todo.uid}.json?_method=DELETE"
method="post"
use:enhance={{
pending: () => (todo.pending_delete = true),
result: () => {
todos = todos.filter((t) => t.uid !== todo.uid);
}
}}
>
<button class="delete" aria-label="Delete todo" disabled={todo.pending_delete} />
</form>
</div>
{/each}
</div>
<style>
.todos {
width: 100%;
max-width: var(--column-width);
margin: var(--column-margin-top) auto 0 auto;
line-height: 1;
}
.new {
margin: 0 0 0.5rem 0;
}
input {
border: 1px solid transparent;
}
input:focus-visible {
box-shadow: inset 1px 1px 6px rgba(0, 0, 0, 0.1);
border: 1px solid #ff3e00 !important;
outline: none;
}
.new input {
font-size: 28px;
width: 100%;
padding: 0.5em 1em 0.3em 1em;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
text-align: center;
}
.todo {
display: grid;
grid-template-columns: 2rem 1fr 2rem;
grid-gap: 0.5rem;
align-items: center;
margin: 0 0 0.5rem 0;
padding: 0.5rem;
background-color: white;
border-radius: 8px;
filter: drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.1));
transform: translate(-1px, -1px);
transition: filter 0.2s, transform 0.2s;
}
.done {
transform: none;
opacity: 0.4;
filter: drop-shadow(0px 0px 1px rgba(0, 0, 0, 0.1));
}
form.text {
position: relative;
display: flex;
align-items: center;
flex: 1;
}
.todo input {
flex: 1;
padding: 0.5em 2em 0.5em 0.8em;
border-radius: 3px;
}
.todo button {
width: 2em;
height: 2em;
border: none;
background-color: transparent;
background-position: 50% 50%;
background-repeat: no-repeat;
}
button.toggle {
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 50%;
box-sizing: border-box;
background-size: 1em auto;
}
.done .toggle {
background-image: url("data:image/svg+xml,%3Csvg width='22' height='16' viewBox='0 0 22 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20.5 1.5L7.4375 14.5L1.5 8.5909' stroke='%23676778' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.delete {
background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4.5 5V22H19.5V5H4.5Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M10 10V16.5' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M14 10V16.5' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M2 5H22' stroke='%23676778' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M8 5L9.6445 2H14.3885L16 5H8Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3C/svg%3E%0A");
opacity: 0.2;
}
.delete:hover,
.delete:focus {
transition: opacity 0.2s;
opacity: 1;
}
.save {
position: absolute;
right: 0;
opacity: 0;
background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20.5 2H3.5C2.67158 2 2 2.67157 2 3.5V20.5C2 21.3284 2.67158 22 3.5 22H20.5C21.3284 22 22 21.3284 22 20.5V3.5C22 2.67157 21.3284 2 20.5 2Z' fill='%23676778' stroke='%23676778' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M17 2V11H7.5V2H17Z' fill='white' stroke='white' stroke-width='1.5' stroke-linejoin='round'/%3E%3Cpath d='M13.5 5.5V7.5' stroke='%23676778' stroke-width='1.5' stroke-linecap='round'/%3E%3Cpath d='M5.99844 2H18.4992' stroke='%23676778' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E%0A");
}
.todo input:focus + .save,
.save:focus {
transition: opacity 0.2s;
opacity: 1;
}
</style>

@ -0,0 +1,9 @@
<script>
import Socials from "$lib/Socials.svelte";
</script>
<svelte:head>
<title>Home</title>
</svelte:head>
<main />

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-auto';
import adapter from "@sveltejs/adapter-auto";
/** @type {import('@sveltejs/kit').Config} */
const config = {
@ -6,8 +6,8 @@ const config = {
adapter: adapter(),
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte'
}
target: "#svelte",
},
};
export default config;

@ -0,0 +1,11 @@
module.exports = {
darkMode: "class",
content: ["./src/**/*.{html,js,svelte,ts}"],
theme: {
container: {
center: true,
},
extend: {},
},
plugins: [],
};
Loading…
Cancel
Save