Compare commits

...

20 Commits

Author SHA1 Message Date
d9045f23c2 added blog post about changing domains 2026-03-26 20:51:13 +01:00
816f9cefec replaced links to this website with natconf.dev 2026-03-26 20:21:39 +01:00
94416f5779 edited about page 2026-03-26 11:11:55 +01:00
7ab4a9a173 edited the button corner on the main page 2026-03-25 22:33:04 +01:00
2d052acf91 added warning banner to outdated postgresql blog post 2026-03-25 22:29:26 +01:00
5dee4fccc1 added new blog post 2026-03-25 22:18:57 +01:00
d0d74becc5 added subtitle to main page 2026-03-25 21:44:49 +01:00
0911a8892f added update about blog post 2026-03-17 17:10:56 +01:00
c95fd2a101 edited styling of code blocks to improve legibility 2026-03-17 17:06:59 +01:00
e118a71f76 added blog post 0317 2026-03-17 17:03:47 +01:00
3c47cbe581 added 88x31 buttons to main page 2026-03-14 22:22:39 +01:00
8c00b77d4e slight text edits to blog post 2026-03-14 17:54:25 +01:00
070e50e718 updated page title on Homesick page where it previously announced the page as Project N5 2026-03-14 10:45:47 +01:00
757510744f updated links to copyparty public share 2026-03-14 09:32:36 +01:00
c63aeabb6c added that homesick was previously called project n5 to the project page. should have done this before 2026-03-11 19:48:06 +01:00
4ff75057da renamed project n5 to homesick on main page 2026-03-11 19:33:44 +01:00
5558e3cf3b added update to link to drawing page 2026-03-11 19:22:34 +01:00
a186a0e4bc changed Project N5 references in some places to Homesick 2026-03-11 18:57:19 +01:00
e094d68dfa removed project n5 link from header; added homesick and drawing page links to footer 2026-03-11 18:54:33 +01:00
72b9d15381 adjusted gallery to scale down on mobile devices 2026-03-11 18:38:32 +01:00
33 changed files with 587 additions and 120 deletions

View File

@@ -1,5 +1,5 @@
# pages-svelte
SvelteKit repository for my website hosted at [https://denizk0461.dev/](https://denizk0461.dev/)
SvelteKit repository for my website hosted at [https://natconf.dev/](https://natconf.dev/)
![Screenshot of the website's main page](website-screenshot.webp)

View File

@@ -0,0 +1,32 @@
<script lang="ts">
import { type IndieButton } from "./indie-button";
let {
button
}: {
button: IndieButton;
} = $props();
</script>
<a class="button" href="{button.link}">
<img src="/common/buttons/{button.img}" alt="{button.alt}">
</a>
<style>
.button {
width: 88px;
height: 31px;
transition: scale var(--duration-animation) var(--anim-curve);
margin: 0;
}
.button:hover {
scale: 1.2;
z-index: 10;
}
.button img {
height: 100%;
width: 100%;
}
</style>

View File

@@ -0,0 +1,26 @@
export interface IndieButton {
img: string;
alt: string;
link?: string;
}
export let buttons: IndieButton[] = [
{
img: "iso8601.png",
alt: "A button with the text 'ISO 8601 YYYY-MM-DD'.",
link: "https://www.iso8601.com/",
},
{
img: "queercoded.png",
alt: "A button with the text \"you're telling me a queer coded this\" on a rainbow background.",
},
{
img: "madewithsveltekit.gif",
alt: "A red button with the text 'MADE WITH SVELTEKIT'.",
link: "https://svelte.dev",
},
{
img: "trans-rights-now.png",
alt: "A button with the text 'TRANS RIGHTS NOW!' next to a trans flag.",
},
];

View File

@@ -80,10 +80,11 @@
}
.toc-container {
max-width: 650px;
max-width: var(--width-toc);
margin-left: auto;
margin-right: auto;
margin-top: 12px;
box-sizing: border-box;
background-color: var(--color-background-highlight);
padding: 16px 0;
border: var(--border-style) var(--border-dash-size) var(--color-highlight);

View File

@@ -22,7 +22,7 @@
</div>
<span class="update-entry-timestamp-divider">::</span>
<p>
{entry.content}
{@html entry.content}
{#if entry.link}
<a class="update-entry-link" href="{entry.link}">»</a>
{/if}

View File

@@ -95,8 +95,6 @@
.gallery-title {
font-family: var(--font-mono);
font-weight: 700;
font-size: 1.4rem;
line-height: 2.0rem;
}
.gallery-subtitle, .gallery-description {
@@ -115,43 +113,43 @@
background-color: var(--color-background-highlight);
backdrop-filter: blur(var(--blur-radius-background));
}
.gallery-container:hover .gallery-img, .gallery-container:hover .gallery-img-placeholder {
/* filter: grayscale(0%); */
/* margin-left: 8px; */
width: 260px;
}
.gallery-container:hover .gallery-text-container {
/* padding-right: 8px; */
grid-template-rows: 0fr 1fr 1fr;
}
.gallery-container:hover p {
color: var(--color-highlight);
}
.gallery-container:hover .gallery-subtitle {
opacity: 0;
}
.gallery-container:hover .gallery-description {
opacity: 1;
}
@media screen and (max-width: 700px) {
/* .gallery-title {
font-size: 1.0rem;
line-height: 1.1rem;
@media screen and (min-width: 800px) {
.gallery-title {
font-size: 1.4rem;
line-height: 2.0rem;
}
.gallery-subtitle {
font-size: 0.9rem;
line-height: 1.1rem;
} */
}
@media screen and (max-width: 500px) {
.gallery-container:hover .gallery-img, .gallery-container:hover .gallery-img-placeholder {
width: 260px;
}
.gallery-container:hover .gallery-text-container {
grid-template-rows: 0fr 1fr 1fr;
}
.gallery-container:hover .gallery-subtitle {
opacity: 0;
}
.gallery-container:hover .gallery-description {
opacity: 1;
}
}
@media screen and (max-width: 800px) {
.gallery-title {
font-size: 1.0rem;
line-height: 1.1rem;
}
.gallery-description {
display: none;
}
.gallery-subtitle {
font-size: 0.8rem;
line-height: 1rem;
@@ -159,7 +157,7 @@
}
.gallery-container {
height: 64px;
height: 72px;
}
.gallery-text-container {
@@ -167,8 +165,8 @@
}
.gallery-img, .gallery-img-placeholder {
width: 100px;
min-width: 100px;
width: 110px;
min-width: 110px;
}
}
</style>

View File

@@ -20,20 +20,22 @@
<div class="content-container">
<div class="content-box center-box">
<p> 20232026 denizk0461</p>
<p>Built from commit <a class="commit" href="https://code.denizk0461.dev/denizk0461/pages/src/commit/{version}">{version.substring(0, 6)}</a></p>
<p>Built from commit <a class="commit" href="https://code.natconf.dev/denizk0461/pages/src/commit/{version}">{version.substring(0, 6)}</a></p>
</div>
<div class="content-box">
<h6>Content</h6>
<a href="/projects">Projects</a>
<a class="link-level-2" href="/projects/projectn5">Homesick</a>
<a href="/blog">Blog</a>
<a href="/art">Art</a>
<a class="link-level-2" href="/art/drawings">Drawings</a>
</div>
<div class="content-box">
<h6>Meta</h6>
<a href="/meta/about">About</a>
<a href="/meta/feeds">Feeds</a>
<a href="/meta/updates">Updates</a>
<a href="https://code.denizk0461.dev/denizk0461/pages">Page Source</a>
<a href="https://code.natconf.dev/denizk0461/pages">Page Source</a>
<a href="/meta/privacy">Privacy & Cookies</a>
</div>
</div>
@@ -67,6 +69,14 @@
margin: 0;
}
.link-level-2 {
padding-left: 16px;
}
.link-level-2::before {
content: '└ ';
}
footer a:hover {
font-weight: 700;
text-decoration: var(--border-style) var(--underline-dash-size) var(--color-highlight) underline;

View File

@@ -1,10 +1,8 @@
{#snippet headerLinks()}
<a href="/">Home</a>
<a href="/projects">Projects</a>
<a href="/projects/projectn5">Project N5</a>
<a href="/blog">Blog</a>
<a href="/art">Art</a>
<!-- <a href="/garden">Garden</a> -->
<a href="/meta/about">About</a>
{/snippet}

View File

@@ -97,12 +97,19 @@
font-style: normal;
}
@font-face {
font-family: "LIGHTYEARS";
src: url("/fonts/lightyears.woff2");
font-weight: 400;
font-style: normal;
}
/* OpenMoji */
@font-face {
font-family: "OpenMoji";
src: url("/fonts/openmoji/openmoji.woff2") format("woff2");
unicode-range: U+23,U+2A,U+2D,U+30-39,U+A9,U+AE,U+200D,U+203C,U+2049,U+20E3,U+2117,U+2120,U+2122,U+2139,U+2194-2199,U+21A9,U+21AA,U+229C,U+231A,U+231B,U+2328,U+23CF,U+23E9-23F3,U+23F8-23FE,U+24C2,U+25A1,U+25AA-25AE,U+25B6,U+25C0,U+25C9,U+25D0,U+25D1,U+25E7-25EA,U+25ED,U+25EE,U+25FB-25FE,U+2600-2605,U+260E,U+2611,U+2614,U+2615,U+2618,U+261D,U+2620,U+2622,U+2623,U+2626,U+262A,U+262E,U+262F,U+2638-263A,U+2640,U+2642,U+2648-2653,U+265F,U+2660,U+2663,U+2665,U+2666,U+2668,U+267B,U+267E,U+267F,U+2691-2697,U+2699,U+269B,U+269C,U+26A0,U+26A1,U+26A7,U+26AA,U+26AB,U+26B0,U+26B1,U+26BD,U+26BE,U+26C4,U+26C5,U+26C8,U+26CE,U+26CF,U+26D1,U+26D3,U+26D4,U+26E9,U+26EA,U+26F0-26F5,U+26F7-26FA,U+26FD,U+2702,U+2705,U+2708-270D,U+270F,U+2712,U+2714,U+2716,U+271D,U+2721,U+2728,U+2733,U+2734,U+2744,U+2747,U+274C,U+274E,U+2753-2755,U+2757,U+2763,U+2764,U+2795-2797,U+27A1,U+27B0,U+27BF,U+2934,U+2935,U+2B05-2B07,U+2B0C,U+2B0D,U+2B1B,U+2B1C,U+2B1F-2B24,U+2B2E,U+2B2F,U+2B50,U+2B55,U+2B58,U+2B8F,U+2BBA-2BBC,U+2BC3,U+2BC4,U+2BEA,U+2BEB,U+3030,U+303D,U+3297,U+3299,U+E000-E009,U+E010,U+E011,U+E040-E06D,U+E080-E0B4,U+E0C0-E0CC,U+E0FF-E10D,U+E140-E14A,U+E150-E157,U+E181-E189,U+E1C0-E1C4,U+E1C6-E1D9,U+E200-E216,U+E240-E269,U+E280-E283,U+E2C0-E2C4,U+E2C6-E2DA,U+E300-E303,U+E305-E30F,U+E312-E316,U+E318-E322,U+E324-E329,U+E32B,U+E340-E348,U+E380,U+E381,U+F000,U+F77A,U+F8FF,U+FE0F,U+1F004,U+1F0CF,U+1F10D-1F10F,U+1F12F,U+1F16D-1F171,U+1F17E,U+1F17F,U+1F18E,U+1F191-1F19A,U+1F1E6-1F1FF,U+1F201,U+1F202,U+1F21A,U+1F22F,U+1F232-1F23A,U+1F250,U+1F251,U+1F260-1F265,U+1F300-1F321,U+1F324-1F393,U+1F396,U+1F397,U+1F399-1F39B,U+1F39E-1F3F0,U+1F3F3-1F3F5,U+1F3F7-1F4FD,U+1F4FF-1F53D,U+1F549-1F54E,U+1F550-1F567,U+1F56F,U+1F570,U+1F573-1F57A,U+1F587,U+1F58A-1F58D,U+1F590,U+1F595,U+1F596,U+1F5A4,U+1F5A5,U+1F5A8,U+1F5B1,U+1F5B2,U+1F5BC,U+1F5C2-1F5C4,U+1F5D1-1F5D3,U+1F5DC-1F5DE,U+1F5E1,U+1F5E3,U+1F5E8,U+1F5EF,U+1F5F3,U+1F5FA-1F64F,U+1F680-1F6C5,U+1F6CB-1F6D2,U+1F6D5-1F6D7,U+1F6DC-1F6E5,U+1F6E9,U+1F6EB,U+1F6EC,U+1F6F0,U+1F6F3-1F6FC,U+1F7E0-1F7EB,U+1F7F0,U+1F90C-1F93A,U+1F93C-1F945,U+1F947-1F9FF,U+1FA70-1FA7C,U+1FA80-1FA89,U+1FA8F-1FAC6,U+1FACE-1FADC,U+1FADF-1FAE9,U+1FAF0-1FAF8,U+1FBC5-1FBC9,U+E0061-E0067,U+E0069,U+E006C-E0079,U+E007F;
}
font-family: "OpenMoji";
src: url("/fonts/openmoji/openmoji.woff2") format("woff2");
unicode-range: U+23,U+2A,U+2D,U+30-39,U+A9,U+AE,U+200D,U+203C,U+2049,U+20E3,U+2117,U+2120,U+2122,U+2139,U+2194-2199,U+21A9,U+21AA,U+229C,U+231A,U+231B,U+2328,U+23CF,U+23E9-23F3,U+23F8-23FE,U+24C2,U+25A1,U+25AA-25AE,U+25B6,U+25C0,U+25C9,U+25D0,U+25D1,U+25E7-25EA,U+25ED,U+25EE,U+25FB-25FE,U+2600-2605,U+260E,U+2611,U+2614,U+2615,U+2618,U+261D,U+2620,U+2622,U+2623,U+2626,U+262A,U+262E,U+262F,U+2638-263A,U+2640,U+2642,U+2648-2653,U+265F,U+2660,U+2663,U+2665,U+2666,U+2668,U+267B,U+267E,U+267F,U+2691-2697,U+2699,U+269B,U+269C,U+26A0,U+26A1,U+26A7,U+26AA,U+26AB,U+26B0,U+26B1,U+26BD,U+26BE,U+26C4,U+26C5,U+26C8,U+26CE,U+26CF,U+26D1,U+26D3,U+26D4,U+26E9,U+26EA,U+26F0-26F5,U+26F7-26FA,U+26FD,U+2702,U+2705,U+2708-270D,U+270F,U+2712,U+2714,U+2716,U+271D,U+2721,U+2728,U+2733,U+2734,U+2744,U+2747,U+274C,U+274E,U+2753-2755,U+2757,U+2763,U+2764,U+2795-2797,U+27A1,U+27B0,U+27BF,U+2934,U+2935,U+2B05-2B07,U+2B0C,U+2B0D,U+2B1B,U+2B1C,U+2B1F-2B24,U+2B2E,U+2B2F,U+2B50,U+2B55,U+2B58,U+2B8F,U+2BBA-2BBC,U+2BC3,U+2BC4,U+2BEA,U+2BEB,U+3030,U+303D,U+3297,U+3299,U+E000-E009,U+E010,U+E011,U+E040-E06D,U+E080-E0B4,U+E0C0-E0CC,U+E0FF-E10D,U+E140-E14A,U+E150-E157,U+E181-E189,U+E1C0-E1C4,U+E1C6-E1D9,U+E200-E216,U+E240-E269,U+E280-E283,U+E2C0-E2C4,U+E2C6-E2DA,U+E300-E303,U+E305-E30F,U+E312-E316,U+E318-E322,U+E324-E329,U+E32B,U+E340-E348,U+E380,U+E381,U+F000,U+F77A,U+F8FF,U+FE0F,U+1F004,U+1F0CF,U+1F10D-1F10F,U+1F12F,U+1F16D-1F171,U+1F17E,U+1F17F,U+1F18E,U+1F191-1F19A,U+1F1E6-1F1FF,U+1F201,U+1F202,U+1F21A,U+1F22F,U+1F232-1F23A,U+1F250,U+1F251,U+1F260-1F265,U+1F300-1F321,U+1F324-1F393,U+1F396,U+1F397,U+1F399-1F39B,U+1F39E-1F3F0,U+1F3F3-1F3F5,U+1F3F7-1F4FD,U+1F4FF-1F53D,U+1F549-1F54E,U+1F550-1F567,U+1F56F,U+1F570,U+1F573-1F57A,U+1F587,U+1F58A-1F58D,U+1F590,U+1F595,U+1F596,U+1F5A4,U+1F5A5,U+1F5A8,U+1F5B1,U+1F5B2,U+1F5BC,U+1F5C2-1F5C4,U+1F5D1-1F5D3,U+1F5DC-1F5DE,U+1F5E1,U+1F5E3,U+1F5E8,U+1F5EF,U+1F5F3,U+1F5FA-1F64F,U+1F680-1F6C5,U+1F6CB-1F6D2,U+1F6D5-1F6D7,U+1F6DC-1F6E5,U+1F6E9,U+1F6EB,U+1F6EC,U+1F6F0,U+1F6F3-1F6FC,U+1F7E0-1F7EB,U+1F7F0,U+1F90C-1F93A,U+1F93C-1F945,U+1F947-1F9FF,U+1FA70-1FA7C,U+1FA80-1FA89,U+1FA8F-1FAC6,U+1FACE-1FADC,U+1FADF-1FAE9,U+1FAF0-1FAF8,U+1FBC5-1FBC9,U+E0061-E0067,U+E0069,U+E006C-E0079,U+E007F;
}
/* #endregion */
@@ -120,7 +127,8 @@
--color-header-highlight: color-mix(in srgb, #6d1e26 80%, transparent);
--color-background: #111111;
--color-background-highlight: color-mix(in srgb, var(--color-highlight) 10%, transparent);
--color-background-highlight: color-mix(in srgb, var(--color-highlight) 20%, transparent);
--color-background-highlight-alt: color-mix(in srgb, var(--color-highlight-alt) 20%, transparent);
--color-background-highlight-hover: color-mix(in srgb, var(--color-highlight) 60%, transparent);
--color-background-highlight-hover-dark: color-mix(in srgb, var(--color-highlight-dark) 60%, transparent);
@@ -154,6 +162,8 @@
--font-mono: 'Kode Mono', 'OpenMoji', monospace;
--font-size-mono: 0.9em;
--font-lightyears: 'LIGHTYEARS', sans-serif;
--font-size-h1: 2.0rem;
--font-size-h2: 1.5rem;
--font-size-h3: 1.3rem;
@@ -164,6 +174,7 @@
/* sizing */
--media-width: 80%;
--width-toc: 650px;
/* page sizing */
--page-width: 1200px;
@@ -305,6 +316,11 @@
padding-right: 6px;
}
.code-block {
width: fit-content;
padding: 16px;
}
img, video {
margin-left: auto;
margin-right: auto;
@@ -326,6 +342,10 @@
border: var(--border-dash-size) var(--border-style) var(--color-highlight); */
}
.lightyears-text {
font-family: var(--font-lightyears);
}
.horizontally-centre-aligned {
width: var(--media-width);
display: flex;
@@ -455,5 +475,15 @@
font-family: var(--font-sans-serif);
padding: 4px;
}
.callout-warning {
margin: 12px auto;
max-width: var(--width-toc);
padding: 12px 20px;
box-sizing: border-box;
backdrop-filter: blur(var(--blur-radius-background));
background-color: var(--color-background-highlight-alt);
border: var(--border-dash-size) var(--border-style) var(--color-highlight-alt);
}
}
</style>

View File

@@ -1,12 +1,13 @@
<script lang="ts">
import Content from "$lib/viewport/content.svelte";
import SubtitledImage from "$lib/components/subtitled-image.svelte";
import GalleryRow, { type GalleryRowEntry } from "$lib/lists/gallery-row.svelte";
import { posts as devlogPosts } from "./projects/projectn5/devlog/posts";
import { posts as blogPosts } from "./blog/posts";
import { entries as updateEntries } from "./meta/updates/updates";
import UpdateEntry from "$lib/components/update-entry.svelte";
import IndieButton from "$lib/components/indie-button.svelte";
import { buttons } from "$lib/components/indie-button";
let latestDevlogDate = devlogPosts[0].post.date;
let latestBlogDate = blogPosts[0].post.date;
@@ -15,7 +16,7 @@
const galleryTopRow: GalleryRowEntry[] = [
{
title: "Project N5 devlog",
title: "Homesick devlog",
description: `My active Godot game project about finding yourself in an unfamiliar future. <i>latest update: ${latestDevlogDate}</i>`,
img: "projects/projectn5/banner2.webp",
altText: "The protagonist Laura standing on a floating platform in the purple test level. Ziplines are all around her and the text 'When this text is spinning, the game is not paused' is frozen in the sky.",
@@ -50,14 +51,14 @@
description: "Find things I've put for download on my Copyparty instance",
img: "main/hypertext.webp",
altText: "Screenshot of Hypertext Unity level. Crates are strewn across the floor, Waluigi is flying in front of the camera, and text such as 'COME AND TRY OUR ALL-NEW BLENDER' and 'omg! it's the brandenburg er tor!' is displayed.",
link: "https://files.denizk0461.dev/",
link: "https://files.natconf.dev/public/",
},
{
title: "Gitea",
description: "I now also self-host a Gitea instance where I am likely migrating all my projects to",
img: "main/magic.webp",
altText: "A 'magic' command written in Java. The command shuts down the computer when ran.",
link: "https://code.denizk0461.dev/",
link: "https://code.natconf.dev/",
},
];
</script>
@@ -69,10 +70,12 @@
<Content>
<h1 class="gradient-title"><i>Moin!</i> ~ welcome to my website :)</h1>
<a href="/blog/2026/0325" class="page-subtitle gradient-title lightyears-text">you can change the world from your bedroom!</a>
<hr>
<div>
<img class="me-img pixelated-img" src="me.webp" alt="Pixelated mirror selfie of the website creator wearing a green shirt, fitting the website theme. The face is obscured." title="hi!">
<p>Hi! I'm Deniz. Welcome to my website! I keep rewriting this introduction but I'm REALLY bad at this type of stuff.</p>
@@ -98,34 +101,45 @@
<GalleryRow entries={galleryTopRow} />
<GalleryRow entries={galleryBottomRow} />
<hr>
<div class="split-container">
<div class="webring-container">
<h4 class="update-header">webrings</h4>
<div class="webring">
<iframe title="bucket webring" id="bucket-webring" style="width: 100%; height: 3rem; border: none;" src="https://webring.bucketfish.me/embed.html?name=denizk0461"></iframe>
</div>
<div class="webring-container">
<div class="webring">
<iframe title="bucket webring" id="bucket-webring" style="width: 100%; height: 3rem; border: none;" src="https://webring.bucketfish.me/embed.html?name=denizk0461"></iframe>
<div class="webring">
<a href="https://www.rainbowcemetery.com/devring/prev.php?id=18">
<img src="/webrings/gamedev/ringprev.png" alt="previous">
</a>
<a href="https://www.rainbowcemetery.com/devring/list.php?id=18">
<img src="/webrings/gamedev/88x31.png" alt="list">
</a>
<a href="https://www.rainbowcemetery.com/devring/next.php?id=18">
<img src="/webrings/gamedev/ringnext.png" alt="next">
</a>
</div>
<map name="w95widget">
<area href="https://baccyflap.com/noai" target="_blank" shape="rect" coords="0,0,308,22" alt="no ai webring" title="no ai webring">
<area href="https://baccyflap.com/noai/?prv&s=dzk" target="_top" shape="rect" coords="56,36,130,58" alt="previous" title="previous">
<area href="https://baccyflap.com/noai/?rnd" target="_top" shape="rect" coords="137,36,211,58" alt="random" title="random">
<area href="https://baccyflap.com/noai/?nxt&s=dzk" target="_top" shape="rect" coords="218,36,292,58" alt="next" title="next">
</map>
<img class="webring-img" usemap="#w95widget" src="/webrings/noai/w95widget.webp" alt="a gray Windows 95 style dialog box titled 'The No AI Webring' with a little icon showing a computer chip in a rubbish bin. beside it are three clickable buttons, labeled Previous, Random... and Next">
</div>
<div class="webring">
<a href="https://www.rainbowcemetery.com/devring/prev.php?id=18">
<img src="/webrings/gamedev/ringprev.png" alt="previous">
</a>
<a href="https://www.rainbowcemetery.com/devring/list.php?id=18">
<img src="/webrings/gamedev/88x31.png" alt="list">
</a>
<a href="https://www.rainbowcemetery.com/devring/next.php?id=18">
<img src="/webrings/gamedev/ringnext.png" alt="next">
</a>
<div class="button-container">
<h4 class="update-header">button corner</h4>
<div class="button-subcontainer">
{#each buttons as button}
<IndieButton button={button} />
{/each}
</div>
<p>to be expanded!</p>
<p class="small-supertext">my own 88x31 button is in the making. ETA: ???</p>
</div>
<map name="w95widget">
<area href="https://baccyflap.com/noai" target="_blank" shape="rect" coords="0,0,308,22" alt="no ai webring" title="no ai webring">
<area href="https://baccyflap.com/noai/?prv&s=dzk" target="_top" shape="rect" coords="56,36,130,58" alt="previous" title="previous">
<area href="https://baccyflap.com/noai/?rnd" target="_top" shape="rect" coords="137,36,211,58" alt="random" title="random">
<area href="https://baccyflap.com/noai/?nxt&s=dzk" target="_top" shape="rect" coords="218,36,292,58" alt="next" title="next">
</map>
<img class="webring-img" usemap="#w95widget" src="/webrings/noai/w95widget.webp" alt="a gray Windows 95 style dialog box titled 'The No AI Webring' with a little icon showing a computer chip in a rubbish bin. beside it are three clickable buttons, labeled Previous, Random... and Next">
</div>
</Content>
<style>
@@ -134,7 +148,7 @@
display: flex;
flex-direction: row;
gap: 16px;
margin-bottom: 16px;
margin: 16px 0;
}
@media screen and (max-width: 900px) {
@@ -144,6 +158,18 @@
}
}
.button-container {
flex: 2;
}
.button-subcontainer {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
align-content: center;
}
.webring {
display: flex;
flex-direction: row;
@@ -162,21 +188,23 @@
margin-right: 12px;
}
.update-container, .info-container {
.update-container, .info-container, .button-container, .webring-container {
padding: 8px 24px;
backdrop-filter: blur(var(--blur-radius-background));
flex: 2;
}
.update-container {
.update-container, .button-container, .webring-container {
border: var(--border-dash-size) var(--color-highlight) var(--border-style);
}
.info-container {
.info-container, .button-container {
border: var(--border-dash-size) var(--color-highlight-alt) var(--border-style);
}
.info-container {
flex: 1;
}
.info-container > h4 {
.info-container > h4, .button-container > h4 {
color: var(--color-highlight-alt);
}
.info-container p, .info-container a {
@@ -190,6 +218,7 @@
flex-wrap: wrap;
align-items: center;
justify-content: center;
flex: 3;
/* flex-wrap: wrap; */
/* gap: 8px; */
padding-top: 16px;
@@ -216,6 +245,12 @@
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
padding-bottom: 12px;
}
.page-subtitle {
/* padding-bottom: 12px; */
width: fit-content;
margin: 4px 0 12px 0;
display: block;
}
</style>

View File

@@ -112,7 +112,7 @@ I didn't have any kind of clear goal for today, but I did enjoy the sketching. A
For the most part, I was just testing thing without sticking to just faces. Most importantly perhaps: no perfectionism! I didn't use my eraser *once!* The focus was on trying more rather than perfecting one single drawing. It did work to my benefit, in a way; I drew bodies, heads, even hands.
I talked to a friend of mine earlier today about drawing. Thing is, she's a pro at it. She's been donig it for ages, she's studying art too, and she does insanely cool paintings. She did give me quite a few tips when we talked, but I did notice that a bit of it was way above my level. At one point, she started sketching a realistic eye, telling me about minute details like the positioning of eyelashes, and while I didn't see a direct connection between her advice and my drawings, I listened and watched in awe as she drew a really good-looking eye from what I assume is just experience and possibly my eyes as a partial reference.
I talked to a friend of mine earlier today about drawing. Thing is, she's a pro at it. She's been doing it for ages, she's studying art too, and she does insanely cool paintings. She did give me quite a few tips when we talked, but I did notice that a bit of it was way above my level. At one point, she started sketching a realistic eye, telling me about minute details like the positioning of eyelashes, and while I didn't see a direct connection between her advice and my drawings, I listened and watched in awe as she drew a really good-looking eye from what I assume is just experience and possibly my eyes as a partial reference.
We also talked about perfectionism and how it can be beneficial to draw without erasing constantly like how I drew [my drawing from two weeks ago](/blog/2026/0129/) using a ballpoint pen instead of a pencil. No going back using that. I did intend to use a gel pen today, but I kind of shied away from it for now. Thinking about it now, a ballpoint pen may be better actually, because it draws finer and less bold lines.
@@ -482,13 +482,7 @@ Hand 1 has pretty thick fingers, but it was my first try so whatever. Hand 4's f
Went pretty well! No idea what I'll do tomorrow, but it's going well. If I'm really daring, I'll do another character (that'll hopefully take less time...) and try to add some cool hands. Jazz hands would be funny ngl
Oh a bit of a tangent: I do have to say that I felt a little down today. There were a lot of good things today: I'm virtually sickness-free, I wore a cool new outfit and felt very gen-Z-internety in it so I listened to I Love My Computer by Ninajirachi on the bus and felt even more in my element, and the weather was super nice today as well. I even met up with friends!
But I wanted to show one of my friends, the artsy one, my drawings today. I don't see her super often so I wanted to use the chance. I was actually really excited to do so because obviously I've put quite a lot of time into these drawings and I'm quite proud of especially what I've managed to accomplish in this short time, and I like sharing that. But she wanted me to show them off while we were busy doing something else entirely, to which I told her something along the lines of *"I'll show you when we have the time for it"* because I wanted to take my time with that rather than rush through the sketches. I ended up not being able to show her my drawings, but she still gave me the advice to *study anatomy*.
That felt pretty discouraging. She hadn't even seen my drawings, doesn't know what or how I am drawing, and gave me pretty boilerplate advice as a result. I guess it's not too dramatic, but it still didn't feel great.
But I'm fine now. In fact, I just looked at my drawing from [two days ago](#day-18) again, and that made me feel really good. I'm *so happy* about that drawing in particular and my progress in general. *I created these drawings!!* How cool is that?? AI images could never evoke the joy in me that I am feeling when I look at my sketches.
Oh a bit of a tangent: I just looked at my drawing from [two days ago](#day-18) again, and that made me feel really good. I'm *so happy* about that drawing in particular and my progress in general. *I created these drawings!!* How cool is that?? AI images could never evoke the joy in me that I am feeling when I look at my sketches.
## Day 21: we'll see creation come undone

View File

@@ -0,0 +1,247 @@
<div class="callout-warning">
<p><b>Note</b>: as I'm constantly learning new things, this blog post is sort of outdated now. The information is still correct, but it feels inefficient and leads into a dead-end. I dislike this, as I wanted to write a guide that provides an expandable base. Therefore, I'll likely either update or replace this article and trim it down to focus on the important bits.</p>
<p>Expect a Go API.</p>
</div>
*Hey!*
Do you want to develop a web application in SvelteKit? Do you want this application to access a PostgreSQL database on a remote server? Do you struggle with CORS (cross-origin resource sharing) and CSRF (cross-site request forgery) and keep receiving error 403? Well, I did, and I couldn't find ***ANYTHING*** on the internet going beyond a test running locally. This is why I'm writing this guide.
Let me preface this by saying: what I did isn't perfect. It's also likely incomplete; I would give bad advice in a complete guide for sure. What this is, is a guide covering the key aspects for setting up a relatively minimal setup to get your application online successfully, upon which you can expand by adding more features. The important distinction from other guides I've found online is that this guide doesn't deploy the backend locally, but instead sets it up online, ready to use.
## The Guide
### What You Need
- a device, such as a laptop or a desktop, with the following installed:
- an IDE
- `npm`
- `ssh` keys for the server, if you're accessing it remotely
- a remote server instance set up with some software:
- Nginx
- Docker Compose
- an idea for what kind of app you wanna make
### Getting Started
#### SvelteKit
For creating a SvelteKit app, I recommend the guide [Getting Started](https://svelte.dev/docs/svelte/getting-started) by the Svelte team. You won't need more than the first four commands to create the app and get it up and running. During the setup, I recommend you pick the `adapter-node`.
#### Backend
For the backend, which will run on your remote server, it's very easy to use Docker Compose to get started. Once you're `ssh`'d into the server (or physically sitting at the terminal), create a folder where the data will live in, then create a file named `compose.yml` (for instance, by typing `nano compose.yml` into the terminal) and paste this into the file, then use `docker compose up -d` (or if that doesn't work, try `docker-compose up -d` with a hyphen) to start the container:
<pre class="code-block">
version: '3'
services:
api:
image: postgrest/postgrest
restart: always
ports:
- 3100:3000
environment:
PGRST_SERVER_HOST: '0.0.0.0'
PGRST_DB_URI: 'postgres://authenticator:password@database:5432/postgres'
PGRST_DB_SCHEMAS: 'api'
PGRST_DB_ANON_ROLE: 'web_anon'
PGRST_SERVER_CORS_ALLOWED_ORIGINS: "https://app.natconf.dev"
depends_on:
- database
database:
image: postgres
restart: always
ports:
- 5432:5432
environment:
POSTGRES_USER: 'username'
POSTGRES_PASSWORD: 'password'
POSTGRES_DB: 'database_name'
volumes:
- ./postgresql:/var/lib/postgresql
</pre>
Important notes:
This `compose.yml` covers both the PostgreSQL database itself as well as a program called [PostgREST](https://docs.postgrest.org/), which connects to a PostgreSQL database and acts as a [REST](https://developer.mozilla.org/en-US/docs/Glossary/REST) api. This is advantageous, as it means you won't need modules such as [Postgres.js](https://github.com/porsager/postgres) to send raw SQL queries from SvelteKit to the database. With PostgREST, you can use a regular URL to access and modify database data using HTTP verbs such as GET (for getting data) and POST (for sending new data).
Some environment variables need to be set: `PGRST_SERVER_CORS_ALLOWED_ORIGINS` decides from which domains requests are allowed. Setting it to `"*"` allows all domains. Change this to the domain your application is running on. Set `POSTGRES_USER` and `POSTGRES_PASSWORD` to the data you'll use to log in as your admin account. Also, `PGRST_DB_ANON_ROLE` is the user that will be used when making requests to the database without credentials. This user's permissions will decide whether GET, POST, etc. requests will go through. You don't need to change this, but if you do, you'll also have to change `web_anon` later in this guide.
In `PGRST_DB_URI`, the link uses the username `authenticator` and a password. Replace the word 'password' with the actual password you'll use for the `authenticator` role. You will set up this role [later](#configure-the-database). This is *not* the same password you'll set for `POSTGRES_PASSWORD`!
The ports may differ on your machine. The `database` service runs on port 5432. You only need to change this if this port is already used by something on your server. Keep in mind to change the **left** number only! The left number is the port that faces outwards, the right port is the one used internally by the container. Since PostgreSQL is running on port 5432 by default, and since there is nothing else running inside the container that's also on port 5432, we don't need to change the right side at all. If you change the left port, be sure to change the port used in the `api` service's `PGRST_DB_URI` as well, as this URI is used by PostgREST to connect to the database.
I did change the port for the `api` service, however. PostgREST runs on port 3000 by default. This is problematic, as Node (which we will use for the SvelteKit application later) also runs on port 3000 by default. I chose to change PostgREST's port to 3100 here to avoid conflict, but you can pick any non-privileged port (any between 1024 and 65535 is good).
Keep in mind that you will not need to open up any of these ports on your firewall. Why? This is why:
#### Nginx
I'm using Nginx as a reverse proxy here. What does this mean? It means that we can send a human-readable URL to Nginx, such as this: `https://files.natconf.dev`, and Nginx will *internally* route us to the place the service is actually running on: `http://localhost:3923`. This has several advantages: as the end user, we won't need to remember the IP address or the port of the services we connect to. As the server administrator, we will only need to expose a minimal number of ports. In this example, port 3923 is actually closed off from the public, meaning that bypassing Nginx and connecting to the service directly, e.g. by typing `https://natconf.dev:3923`, is impossible. The only open port is 443, which is a standard port for websites served over encrypted HTTPS. Your browser will always try to connect to a website starting with `https://` on port 443, unless you specify another port. Port 80 is actually also open on my server, as it accepts requests through the unsafe `http://`, but it only redirects to `https://` on 443.
I configured Nginx to run both the SvelteKit app as well as the PostgREST backend from the same subdomain. You don't *need* to do this, and I *think* it should even work if you put, for example, the database on a different subdomain, but I can't guarantee it.
<!-- &#123; == {. if this is not encoded, sveltekit will try to interpret it as code and throw an error -->
<pre class="code-block">
server &#123;
listen 443;
server_name app.natconf.dev;
location / &#123;
proxy_pass http://localhost:3000;
}
location /customers &#123;
proxy_pass http://localhost:3100/customers;
}
}
</pre>
This `server` block serves two functions:
- it serves our SvelteKit app, which by default runs on port 3000, from the root directory of the subdomain (e.g. `https://app.natconf.dev`), and
- it serves the table `customers` from our PostgreSQL database, which is accessible through PostgREST on port 3100, on the subpage `/customers` (`https://app.natconf.dev/customers`).
- If your table has a different name, replace `customers` with that table's name.
- If you want multiple tables to be accessible, copy the entire `location /[tablename]` block and paste it in as often as you need, replacing `[tablename]` with the names of the tables.
Be sure to change `natconf.dev` to your own domain, as this will otherwise not work.
### Configure the Database
Once you have the database running, we need to create a table to store data in. You can log into the database on your server by running this command to enter the PostgreSQL console: `docker exec -it [service-name] psql -d postgres -U [username]`. `[container-name]` is the name of the PostgreSQL service, which you will see popping up in the terminal after running `docker compose up -d`. It will likely look something like `foldername_database`. `[username]` is the name of the privileged user defined in `compose.yml` under the environment variable `POSTGRES_USER`. This is basically your admin account.
You're best off following [the official guide for PostgREST](https://docs.postgrest.org/en/v14/tutorials/tut0.html#step-3-create-database-for-api). Important steps: create the schema, name it `api` (that's the publicly-accessible schema because we set `PGRST_DB_SCHEMAS: 'api'` in the `compose.yml`), create a table (name has to start with `api.` but can be anything after that, e.g. `api.customers`), insert test data, create the roles `web_anon` and `authenticator` and then quit by typing `\q`. That's all. Welcome back!
You can now access the database data on `https://app.natconf.dev` (if you replace my domain with yours). Keep in mind that the PostgREST guide only grants read access (GET) to the database; in order to POST data to the database, you need to grant `INSERT` privileges to `web_anon`: `grant insert on api.customers to web_anon;`.
If you want a kind voice to guide you through this process, I found [Ian Wootten's video on setting up PostgREST](https://youtu.be/RxuofiZNhtU) to be very nice to follow along.
### POST Data From SvelteKit
The difficult part is done. Sending data from SvelteKit to be inserted into the database is pretty easy, fortunately. You can use `fetch` to do this:
<pre class="code-block">
const response = await fetch(`https://$&#123;API_HOST}/$&#123;API_DB}`, &#123;
method: "POST",
headers: &#123;
"Content-Type": "application/json",
},
body: JSON.stringify(customer),
});
if (response.ok) &#123;
console.log("success");
} else &#123;
console.log("an error occurred: " + response.statusText);
}
</pre>
In this example, I set up two environment variables: `API_HOST` and `API_DB`. Those are secrets that won't be published to your Git provider when you push your changes (if your .gitignore is set up correctly!). `API_HOST` is the fully-qualified domain, e.g. `https://app.natconf.dev`, and `API_DB` is the name of the table, e.g. `customers`. Doing this is not necessary, but it's good practice to keep secrets, especially once you get to the stage of needing passwords or authentication keys. If you want to use environment variables, create a file named `.env` in the root directory of your application, add the variables as such:
<pre class="code-block">
API_HOST="https://app.natconf.dev"
API_DB="customers"
</pre>
...and then import them into your script:
<pre class="code-block">
import &#123;
API_HOST,
API_DB,
} from '$env/static/private';
</pre>
Keep in mind that the `fetch` call is async, meaning that if you wrap this in a function, you will have to declare the function as such, e.g. `async function insertCustomer(...)` and call it using await: `await insertUser(...)`.
Also, the `customer` variable I declared is basically one database entry with the exact same properties as you set it up in PostgreSQL, except for the primary key (likely named `id`). You shouldn't send this in a POST request, as `id` will be auto-generated by the database to be a unique value. That is, unless you *want* to generate these values yourself, in which case, go ahead.
This should now work fine to POST data to the database...
### Fix Error 403 On SvelteKit
...except in production. Once you deploy the application on your server, you may face error *'403: Cross-site POST form submissions are forbidden'*. This is an issue caused by a security measure implemented in the browser that forbids accepting data unless the server sends the correct headers. You will only see this issue in a production build as SvelteKit disables these security measures when you're running a development build. The way I fixed this in production is by adding the domain of my backend to the `trustedOrigins` inside the `kit` block of my `svelte.config.js`:
<pre class="code-block">
kit: &#123;
adapter: adapter(),
csrf: &#123;
trustedOrigins: [
"https://app.natconf.dev"
],
},
},
</pre>
**This is the crucial bit.** Only by adding the URL of my backend to the `trustedOrigins` was I able to fix the 403 errors. Is this the correct way of doing it? Frankly, I don't know. But I do know that you should definitely not put `"*"` as one of the trusted origins, as this would mean your app accepts data from anywhere, and that is *definitely* unsafe.
If you just came here to get a solution for the 403 error that's it! We're done :) But read on for an extra piece of advice as well as my struggle to get this working.
### Your Data Will Be Public
**Important:** the contents of the database will be publicly visible, as we've granted GET (and possibly INSERT) permissions to `web_anon`. You will need to do a little more work to create a safe API that only exposes required data. Use either [PostgREST's guide](https://docs.postgrest.org/en/v14/tutorials/tut1.html) or [Ian Wootten's video](https://youtu.be/RxuofiZNhtU) for guidance on how to restrict and grant access safely.
## The Rant
If you only came for the guide, you've finished it. What follows is me ranting about how difficult it was to get this information.
### Why Did I Write This?
I was building a small SvelteKit application for a project I'm doing together with a friend. I figured it'd be easy, as it's just like building my website, except I'll also make some requests to a backend on my server. I was right... kind of. I have spent *two days* trying to figure out how to do this successfully.
The requests were working fine when I was running the development build of the SvelteKit app on my PC. Once I deployed it though, I was facing *'403: Cross-site POST form submissions are forbidden'* errors for every single request. I couldn't figure out why. I scoured the internet for answers, trying anything I could. I configured `Access-Control-Allow-*` headers in Nginx. I set the `ORIGIN` parameter in the app's `.env`. I ran the app and the database on the same subdomain. Nothing worked. When I tried looking for tutorials, there was *nothing* explaining how I could set up what I had in mind.
How could this be? I'm doing web development 101 here! This isn't some advanced toolchain I'm using, it's the bare minimum for a working application with a backend! Somehow, no resource I could find covered this. Any website I read and any video I watched only ever ran those services locally. Docker containers were spun up on the development client, the client apps were run in development mode (which disables CORS/CSRF security features), and there was never a *live* example given.
### Stupid Gaslighting AI Slop
These days, when looking up tech problems in particular, most of the online resources are AI-generated slop. It was genuinely extremely difficult to avoid them, as they were not just prevalent, but almost entirely dominating the search results. There was this one page that I particularly despised. The worst part? It was actually several pages.
If you stumble upon a page such as `w3tutorials.net` or `tutorialpedia.org`, *run*. Do not take in their information. Avoid at all costs. In fact, these pages both use the very same CSS styling with their dark blue background and fade-in title animation upon page load, and you will find even more domains using this exact same template.
But why run? Is it really that bad? Maybe the information is useful?
I'm going to prove why you should avoid generative AI at all costs. Let's go through the article I read: "How to Fix 'Cross-site POST Form Submissions Are Forbidden' Error in SvelteKit: Dev Server Works, Build Fails". Extremely long title, but it in fact describes my exact problem. Four 'fixes' are given:
1. Use Relative Form Actions
This 'fix' describes using relative paths to access form URLs. Instead of `https://app.natconf.dev/customers`, I should just use `/customers`.
This doesn't work.
2. Include the CSRF Token
This 'fix' explains that the issue may be caused by the lack of a CSRF token included in the form that would verify the validity of the submission. The solution is to import the token into the app using `import { csrfToken } from '$app/stores';` and then including the token in a hidden `<input>` named `_csrf`.
*What in the hell* did this AI smoke to hallucinate this? There is no such thing as an importable CSRF token, and it wouldn't make sense for the solution to just be 'send the token along with the request'. Who's validating the token? How would they know it's valid? You would need server-side validation of the token, which is a real thing, but you'd need to code that yourself or use a library for it. You can't just send a random string and say "this request is real, here's some random text to prove it".
3. Configure `ORIGIN` and Environment Variables
Ignoring the fact that their markdown styling doesn't work correctly and shows the \` marks on text that is supposed to be monospaced, this 'fix' alleges that setting the `ORIGIN` environment variable needs to be set to the domain the application is running on so that SvelteKit is aware of this domain.
This *may* work? SvelteKit documentation does [make mention of an `ORIGIN` variable](https://svelte.dev/docs/kit/adapter-node#Environment-variables-ORIGIN-PROTOCOL_HEADER-HOST_HEADER-and-PORT_HEADER) used in the Node adapter. However, as far as I could tell, this had no effect on my app.
4. Check Server Hooks (Handle Function)
This 'fix' accuses you, the developer, of explicitly deleting the required headers for CSRF validation using code such as: `delete event.request.headers.origin;`. *Excuse me?* You little piece of shit. How dare you accuse me of something like this? No, of course I haven't explicitly deleted the headers from my request!
Actually, now that I think about it, this is a very odd thing for an AI to produce. Usually, they are overly positive towards the user. I guess this one was still positively phrased and only indirectly accusing the reader of the mistake. Still, the implication is ridiculous.
### No Surprise Stack Overflow is Dying
Alas, I couldn't find anything helping my cause. In my most desperate moment, I came up with an idea I never thought I'd pursue: *asking a question on Stack Overflow*. **THE** Stack Overflow! The place where arrogant people get high on the most minuscule amounts of power or so I've heard. It can't be that bad.
I wasn't entirely sure whether my problem lied in my SvelteKit, Nginx, or PostgreSQL setup, so I documented them briefly but with the necessary detail in my question. I described my plan and my previous attempts at fixing the issue at hand, including links to the resources I had used. I formatted the entire thing and structured it so that it would be relatively simple to digest. It also had a concluding question to summarise my problem at the end. I read their entire stupid article on 'writing a good question'. What a ridiculous article.
I submitted it to the Staging Ground, which is apparently a Stack Overflow-exclusive thing where new users are forced (?) to submit their questions for 'experienced' users to judge them (which is such a fucking weird concept) and give advice. Fair enough, I thought, I have a fairly open mind and I'd gladly receive constructive feedback so that others could answer my question better.
What instead happened was that within less than 10 minutes of posting my question, two users deemed my question 'off-topic' with the reason 'this question is not about programming or software development' and closed it before it went public. wow. One of them at least had the decency of giving me the advice to post in either Server Fault or Database Administrators. Seeing that this *definitely* wasn't a database problem, I deemed the latter suggestion misguided at best, but I did try Server Fault instead. I copy-pasted my question, submitted it directly (they had no such thing as a Staging Ground), and proceeded to receive no answers.
The best part: once I figured out the solution to my problem on my own, it became abundantly clear that my problem was caused by SvelteKit. This means:
- the question was actually off-topic on Server Fault
- the question was indeed correct to be posted on Stack Overflow
- whoever judged my question to be 'off-topic' likely only wanted to exercise their virtual 'power' and didn't even bother reading and understanding my question while expecting me to read paragraphs on 'how to ask a good question'
You know what? Stack Overflow can burn in hell. No wonder its user base is continually shrinking. What a horrible, toxic place. I'm saying this mostly because of the things I've *heard* about Stack Overflow, but my short first-hand experience clearly showed that those things are true.

View File

@@ -0,0 +1,32 @@
<p class="lightyears-text">The world looks so different now. The world looks so different now. The world looks so different now.</p>
## Background
One of my favourite music artists, [Jaron](https://youtu.be/GXvqQ5-P82I), released his album LIGHTYEARS a little over a year ago. For his visuals, he uses a variety of symbols in place of Latin letters, and there's a converter on [his website](https://jaronsteele.com/) too. Only problem is that my browser can't seem to display most of the characters because the characters aren't included in most fonts.
That's why I made a font! It allows you to type Latin characters from `A-Z` as well as numbers `0-9` and `!?` in the LIGHTYEARS style. Like this:
<p class="lightyears-text">trans rights!</p>
The font exclusively uses characters from the Noto font family. Many of the Noto varieties have been stitched together to recreate the whole LIGHTYEARS alphabet.
To create the font, I used [FontForge](https://fontforge.org). Finding this tool was both a blessing and a curse, as it was exactly what I needed, but it kept. crashing. all. the. time. I tried both the AppImage as well as the release on `dnf` and both had the same issues. I managed to make it work, but it took a lot of patience. Eventually I figured out that importing Noto Maths gave me a 3-8 second window before the editor crashed. The project file would forget the imported font, but if I had copied any glyphs it would keep those.
## Download & use
[Download the font here](https://files.natconf.dev/public/lightyears.woff2). It's in the web-optimised `woff2` format and has most characters stripped to minimise its file size it's less than 20 kilobytes in size! Uppercase and lowercase letters are the same.
For use on your website, put the font into your resources/static/similar folder and then add this block of code to your CSS file:
<pre class="code-block">
@font-face &#123;
font-family: "LIGHTYEARS";
src: url("/fonts/lightyears.woff2");
font-weight: 400;
font-style: normal;
}
</pre>
Then you can change any element's font by setting `font-family: 'LIGHTYEARS', sans-serif;`. Because it's the Latin characters and Arabic numbers that have been changed, you can type text in regular English and people can 'decrypt' the messages by copy-pasting the text somewhere else!
You do **not** need Jaron's converter to type stylised text! The converter returns the actual symbols from other scripts, whereas this font only changes how letters look.

View File

@@ -0,0 +1,7 @@
I'm switching my website domain from `denizk0461.dev` to `natconf.dev`!
All I can say for now about this change is that I came up with this domain name a while back and I liked it more so than `denizk0461.dev` , so I've been thinking about migrating.
I'm updating things as I go. All links on this website now point to the new domain, and all services have been moved over too. As of right now, they're also still accessible via the old domain, but I will be disabling that soon. Instead, I'll set up redirects, which will be in place until the domain expires on 2026-06-04.
I *may* change up some visual elements in the process, but it won't be a major redesign.

View File

@@ -4,7 +4,7 @@ import { posts } from "../posts";
const xml = () => `<rss version="2.0">
<channel>
<title>denizk0461's Blog</title>
<link>https://denizk0461.dev/blog/</link>
<link>https://natconf.dev/blog/</link>
<description><![CDATA[denizk0461 blogs about stuff here]]></description>${getEntries()}
</channel>
</rss>`;
@@ -16,8 +16,8 @@ function getEntries(): String {
<item>
<title><![CDATA[${entry.post.title}]]></title>
<description><![CDATA[${entry.post.description}]]></description>
<link>https://denizk0461.dev/blog/${entry.key}</link>
<guid isPermaLink="true">https://denizk0461.dev/blog/${entry.key}</guid>
<link>https://natconf.dev/blog/${entry.key}</link>
<guid isPermaLink="true">https://natconf.dev/blog/${entry.key}</guid>
<pubDate><![CDATA[${new Date(`${entry.post.date}T${entry.post.time}`).toUTCString()}]]></pubDate>
</item>`)
entries.forEach(entry => {

View File

@@ -23,6 +23,39 @@ export interface BlogPostLink {
export const posts: BlogPostLink[] = [
{
key: "2026/0326",
post: {
date: "2026-03-26",
time: "20:50",
banner: "banner.webp",
bannerAlt: "White light blurs on a darker background.",
title: "Moving On",
description: "It's time to switch domains.",
}
},
{
key: "2026/0325",
post: {
date: "2026-03-25",
time: "22:22",
banner: "banner.webp",
bannerAlt: "A sunset captured from an Autobahn exit.",
title: "I made a LIGHTYEARS font",
description: "I feel electric and it's only getting brighter!",
}
},
{
key: "2026/0317",
post: {
date: "2026-03-17",
time: "17:00",
banner: "banner.webp",
bannerAlt: "A Microsoft Surface Pro 8 displaying a Blue Screen of Death.",
title: "How To: Set Up SvelteKit Frontend + PostgreSQL Backend",
description: "How to set up a web application with a backend on a remote server without getting error 403.",
}
},
{
key: "2026/0214",
post: {

View File

@@ -78,7 +78,7 @@
},
{
text: "Bluesky",
link: "https://bsky.app/profile/denizk0461.dev",
link: "https://bsky.app/profile/natconf.dev",
},
{
text: "Codeberg",
@@ -151,7 +151,7 @@
<img alt="Screenshot of the taskbar of a Fedora KDE setup. There are multiple icons. From left to right: Clank as the application launcher icon, Firefox, fooyin music player, Dolphin file explorer." src="taskbar.webp">
<p>As for the server infrastructure: the website is hosted on a Hetzner server instance I am renting. It's a relatively cheap CPX22 node that costs me 7,72€ a month, and besides my website, it's also hosting things such as a Nextcloud instance and a Minecraft server. In order to host and update the website, I wrote a small bash script that pulls the changes from the <a href="https://code.denizk0461.dev/denizk0461/pages">Git repository</a>, builds the website as a Node server, and then exposes it via Nginx.</p>
<p>As for the server infrastructure: the website is hosted on a Hetzner server instance I am renting. It's a relatively cheap CPX22 node that costs me 7,72€ a month, and besides my website, it's also hosting things such as a Nextcloud instance and a Minecraft server. In order to host and update the website, I wrote a small bash script that pulls the changes from the <a href="https://code.natconf.dev/denizk0461/pages">Git repository</a>, builds the website as a Node server, and then exposes it via Nginx.</p>
<h4 id="software">Tools & Software</h4>
@@ -169,7 +169,7 @@
<p>The <a href="https://ratchetandclank.fandom.com/wiki/Ratchet">rat</a> in the bottom right of the screen is property of <a href="https://insomniac.games/">Insomniac Games</a>. Clicking it will bring you good fortune.</p>
<p>The style of the webring elements is adapted from a template provided by Rainbow Cemetery for the <a href="https://www.rainbowcemetery.com/devring/">Gamedev webring</a>. I adapted it into <a href="https://code.denizk0461.dev/denizk0461/pages/src/branch/master/src/lib/components/ring.svelte">a Svelte component</a> that allows setting the links, emojis, and arrows more easily.</p>
<p>The style of the webring elements is adapted from a template provided by Rainbow Cemetery for the <a href="https://www.rainbowcemetery.com/devring/">Gamedev webring</a>. I adapted it into <a href="https://code.natconf.dev/denizk0461/pages/src/branch/master/src/lib/components/ring.svelte">a Svelte component</a> that allows setting the links, emojis, and arrows more easily.</p>
<h2 id="contact">Contact / Where to find me</h2>
@@ -177,7 +177,7 @@
<LinkList entries={links} />
<p>I removed my Discord username because I don't want to use Discord anymore, now that they <a href="https://www.zdnet.com/article/discord-age-verification-requirement/">ask for facial data and/or government ID that they force to be scanned by third-party AI</a> and then <a href="https://arstechnica.com/tech-policy/2026/02/discord-faces-backlash-over-age-checks-after-data-breach-exposed-70000-ids/">leak it...</a> Apparently they are also about to be <a href="https://www.zdnet.com/article/microsoft-may-be-poised-to-buy-its-next-community-discord/">bought up by Micro$lop</a>?? Discord is as good as dead to me.</p>
<p>I removed my Discord username because I don't want to use Discord anymore, now that they <a href="https://www.zdnet.com/article/discord-age-verification-requirement/">ask for facial data and/or government ID that they force to be scanned by third-party AI</a> and then <a href="https://arstechnica.com/tech-policy/2026/02/discord-faces-backlash-over-age-checks-after-data-breach-exposed-70000-ids/">leak it...</a> Apparently they are also about to be <a href="https://www.zdnet.com/article/microsoft-may-be-poised-to-buy-its-next-community-discord/">bought up by Micro$lop</a>?? I prefer not to use Discord anymore, although I will admit I don't have any good alternative at the moment.</p>
<h2>Anything else?</h2>
@@ -201,7 +201,7 @@
<hr>
<p><i>Last updated: 2026-02-25</i></p>
<p><i>Last updated: 2026-03-26</i></p>
</Content>
<style>

View File

@@ -9,7 +9,7 @@
link: "/blog/feed.xml",
},
{
text: "Project N5 devlog",
text: "Homesick devlog",
link: "/projects/projectn5/devlog/feed.xml",
},
]

View File

@@ -13,7 +13,7 @@
<p>This page uses <b>no cookies</b> as of now. No data will be stored on your device while browsing this website. <b>No trackers</b> are used either <b>no analytics</b>, not even a visit counter of any kind. Not by a third-party, and currently, none I built myself either.</p>
<p>The Godot and Unity projects on the <code>apps.denizk0461.dev</code> subdomain <i>may</i> cache compiled shaders on your device, I'm not sure. These files would only be used by your GPU to render visual effects for the game they were compiled for.</p>
<p>The Godot and Unity projects on the <code>apps.natconf.dev</code> subdomain <i>may</i> cache compiled shaders on your device, I'm not sure. These files would only be used by your GPU to render visual effects for the game they were compiled for.</p>
<p>Last updated: 2025-09-10</p>
</Content>

View File

@@ -1,6 +1,30 @@
import { type UpdateEntry } from "$lib/components/update-entry.svelte";
export const entries: UpdateEntry[] = [
{
date: "2026-03-26",
time: "20:50",
content: "<b>Important</b>: my website is changing domains.",
link: "/blog/2026/0326",
},
{
date: "2026-03-25",
time: "22:22",
content: "I made a LIGHTYEARS font!",
link: "/blog/2026/0325",
},
{
date: "2026-03-17",
time: "17:10",
content: "a bit uncharacteristic, but I wrote a guide on setting up a SvelteKit app + backend because I found NOTHING of the sort online.",
link: "/blog/2026/0317",
},
{
date: "2026-03-11",
time: "19:21",
content: "new page: my drawings!",
link: "/art/drawings",
},
{
date: "2026-03-07",
time: "14:12",
@@ -51,7 +75,7 @@ export const entries: UpdateEntry[] = [
date: "2026-02-03",
time: "15:48",
content: "Now running my own Gitea instance! It now also hosts my website repository.",
link: "https://code.denizk0461.dev/denizk0461/pages",
link: "https://code.natconf.dev/denizk0461/pages",
},
{
date: "2026-02-02",

View File

@@ -29,16 +29,16 @@
<svelte:head>
<title>Project N5 | denizk0461</title>
<title>Homesick | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="Project N5"
title="Homesick"
banner="/projects/projectn5/banner2.webp"
bannerAlt="The protagonist Laura standing on a floating platform in the purple test level. Ziplines are all around her and the text 'When this text is spinning, the game is not paused' is frozen in the sky." />
<p>I am currently working on a game under the working title <b>Project N5</b>! I'm aiming for it to be an action-adventure jump-and-run game inspired by games such as Ratchet & Clank. Development started on <b>2023-09-16</b> and rebooted on <b>2025-05-16</b>.</p>
<p>I am currently working on a game under the working title <b>Homesick</b> (fka Project N5)! I'm aiming for it to be an action-adventure jump-and-run game inspired by games such as Ratchet & Clank. Development started on <b>2023-09-16</b> and rebooted on <b>2025-05-16</b>.</p>
<h2 id="devlog">Development Log</h2>

View File

@@ -4,19 +4,19 @@
let builds: LinkEntry[] = [
{
text: "2023-10-07 (Protagonist #1)",
link: "https://files.denizk0461.dev/projectn5/2023-10-07.zip",
link: "https://files.natconf.dev/public/projectn5/2023-10-07.zip",
},
{
text: "2023-12-23 (Protagonist #2)",
link: "https://files.denizk0461.dev/projectn5/2023-12-23.zip",
link: "https://files.natconf.dev/public/projectn5/2023-12-23.zip",
},
{
text: "2024-03-25 (Protagonist #3 with jump animations)",
link: "https://files.denizk0461.dev/projectn5/2024-03-25.zip",
link: "https://files.natconf.dev/public/projectn5/2024-03-25.zip",
},
{
text: "2025-08-16 (Laura era) [same build as the web version]",
link: "https://files.denizk0461.dev/projectn5/2025-08-16.zip",
link: "https://files.natconf.dev/public/projectn5/2025-08-16.zip",
},
];
</script>
@@ -33,9 +33,9 @@ The good news is that I'm done with all of this! The bachelor's thesis in partic
I decided to upload some playable builds of **Project N5**! Now that the website is running on my own server instead of being hosted by GitHub or Codeberg, I have a lot more freedom here.
The game, in its state from 2025-05-16 (before the reboot), is available to play in-browser [right here!](https://apps.denizk0461.dev/projectn5) It's not a terribly great experience, though. Loading times are significantly longer and shader compilation regularly freezes the game for longer than in a locally-saved copy. Some shaders are also not functioning as intended, though this only has a minor visual impact. The game was never optimised to work on the web, after all.
The game, in its state from 2025-05-16 (before the reboot), is available to play in-browser [right here!](https://apps.natconf.dev/projectn5) It's not a terribly great experience, though. Loading times are significantly longer and shader compilation regularly freezes the game for longer than in a locally-saved copy. Some shaders are also not functioning as intended, though this only has a minor visual impact. The game was never optimised to work on the web, after all.
I've also uploaded old builds of the game [here](https://files.denizk0461.dev/projectn5). You'll find the following builds, one for each protagonist:
I've also uploaded old builds of the game [here](https://files.natconf.dev/public/projectn5). You'll find the following builds, one for each protagonist:
<LinkList entries={builds} />

View File

@@ -16,7 +16,7 @@
<Banner2
title="{data.title}"
subtitle="Project N5 Devlog"
subtitle="Homesick Devlog"
date="{data.date}"
banner="preview.webp"
bannerAlt="{data.bannerAlt}"

View File

@@ -4,7 +4,7 @@ import { posts } from "../posts";
const xml = () => `<rss version="2.0">
<channel>
<title>Project N5 Devlog</title>
<link>https://denizk0461.dev/projects/projectn5/devlog/</link>
<link>https://natconf.dev/projects/projectn5/devlog/</link>
<description><![CDATA[Development log for the game Project N5 by denizk0461]]></description>${getEntries()}
</channel>
</rss>`;
@@ -16,8 +16,8 @@ function getEntries(): String {
<item>
<title><![CDATA[${entry.post.title}]]></title>
<description><![CDATA[${entry.post.description}]]></description>
<link>https://denizk0461.dev/projects/projectn5/devlog/${entry.key}</link>
<guid isPermaLink="true">https://denizk0461.dev/projects/projectn5/devlog/${entry.key}</guid>
<link>https://natconf.dev/projects/projectn5/devlog/${entry.key}</link>
<guid isPermaLink="true">https://natconf.dev/projects/projectn5/devlog/${entry.key}</guid>
<pubDate><![CDATA[${new Date(entry.post.date).toUTCString()}]]></pubDate>
</item>`)
entries.forEach(entry => {

View File

@@ -65,10 +65,10 @@ export const games: Project[] = [
banner: "/projects/projectn5/banner2.webp",
icon: "",
date: "September 2023 now",
title: "Project N5",
title: "Homesick",
subtitle: "",
paragraphs: [
"I'm currently working on a game developed using Godot, entitled Project N5! It's aiming to be an action-adventure 3D jump & run heavily inspired by games such as <a href='https://en.wikipedia.org/wiki/Ratchet_%26_Clank'>Ratchet & Clank</a>.",
"I'm currently working on a game developed using Godot, entitled Homesick! It's aiming to be an action-adventure 3D jump & run heavily inspired by games such as <a href='https://en.wikipedia.org/wiki/Ratchet_%26_Clank'>Ratchet & Clank</a>.",
"I maintain a development log, feel free to check it out if you're curious! Or play some of the old builds available for download below.",
],
links: [
@@ -77,12 +77,12 @@ export const games: Project[] = [
link: "/projects/projectn5",
},
{
text: "Play an <b>old build</b> (developed until 2025-05-16)",
link: "https://apps.denizk0461.dev/projectn5",
text: "Play an <b>old web build</b> (developed until 2025-05-16)",
link: "https://apps.natconf.dev/projectn5",
},
{
text: "Download the <b>old Windows builds</b>",
link: "https://files.denizk0461.dev/projectn5",
link: "https://files.natconf.dev/public/projectn5",
},
],
status: ProjectStatus.ACTIVE,
@@ -102,7 +102,7 @@ export const games: Project[] = [
links: [
{
text: "View the latest <b>Magician</b> build",
link: "https://apps.denizk0461.dev/magician",
link: "https://apps.natconf.dev/magician",
},
],
status: ProjectStatus.ABANDONED,
@@ -139,7 +139,7 @@ export const games: Project[] = [
links: [
{
text: "Play <b>Swords & Stuff</b>",
link: "https://apps.denizk0461.dev/swordsnstuff",
link: "https://apps.natconf.dev/swordsnstuff",
},
],
status: ProjectStatus.ABANDONED,
@@ -159,11 +159,11 @@ export const games: Project[] = [
links: [
{
text: "Play <b>TADS 1</b>",
link: "https://apps.denizk0461.dev/tads/1",
link: "https://apps.natconf.dev/tads/1",
},
{
text: "Play <b>TADS 2</b>",
link: "https://apps.denizk0461.dev/tads/2",
link: "https://apps.natconf.dev/tads/2",
},
],
status: ProjectStatus.FINISHED,
@@ -189,7 +189,7 @@ export const hardware: Project[] = [
},
{
text: "Get the <b>PCB and STL files</b>",
link: "https://files.denizk0461.dev/daisyfm/",
link: "https://files.natconf.dev/public/daisyfm/",
},
{
text: "View the code files on <b>Codeberg</b>",
@@ -317,7 +317,7 @@ export const music: Project[] = [
links: [
{
text: "Listen & download on my <b>copyparty</b> instance",
link: "https://files.denizk0461.dev/my_tracks/Dreamworld/",
link: "https://files.natconf.dev/public/my_tracks/Dreamworld/",
},
],
status: ProjectStatus.FINISHED,
@@ -337,7 +337,7 @@ export const music: Project[] = [
links: [
{
text: "Listen & download on my <b>copyparty</b> instance",
link: "https://files.denizk0461.dev/my_tracks/A%20New%20Beginning/",
link: "https://files.natconf.dev/public/my_tracks/A%20New%20Beginning/",
},
],
status: ProjectStatus.FINISHED,

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 B

Binary file not shown.