Compare commits

...

11 Commits

5 changed files with 273 additions and 20 deletions

View File

@@ -0,0 +1,131 @@
<script lang="ts">
import { BlogPostTag, type BlogPostLink } from "../../routes/blog/posts";
let {
posts,
}: {
posts: BlogPostLink[];
} = $props();
</script>
<div class="entry-container">
{#each posts as post}
<a class="entry" href="{post.key}">
<div class="entry-banner-container">
<img class="entry-banner" src="{post.key}/{post.post.banner}" alt="{post.post.bannerAlt}">
</div>
<div class="entry-text-container">
<p class="entry-title">{post.post.title}</p>
<p class="entry-date">::&nbsp;{post.post.date}&nbsp;::</p>
<p class="entry-description">{post.post.description}</p>
<div class="entry-tag-container">
{#each post.post.tags as tag}
<span class="post-tag">{tag}</span>
{/each}
</div>
</div>
</a>
{/each}
</div>
<style>
.entry-container {
display: grid;
/* gap: 8px; */
grid-template-columns: 1fr 1fr 1fr;
}
.entry {
margin: 0;
padding: 8px;
transition: background-color var(--duration-animation) var(--anim-curve),
border-color var(--duration-animation) var(--anim-curve),
backdrop-filter var(--duration-blur) var(--anim-curve),
border-radius var(--duration-animation) var(--anim-curve);
border: var(--border-dash-size) var(--border-style) transparent;
text-decoration: none;
border-radius: 24px;
}
.entry:hover {
background-color: var(--color-background-highlight-alt);
border-color: var(--color-highlight-alt);
backdrop-filter: blur(var(--blur-radius-background));
}
.entry:hover .entry-banner {
scale: 1.2;
}
.entry:hover .entry-banner-container {
/* border-radius: 24px 24px 0 0; */
border-top-left-radius: 16px;
border-top-right-radius: 16px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.entry-banner-container {
width: 100%;
height: 160px;
overflow: hidden;
display: flex;
justify-content: center;
transition: border-radius var(--duration-animation) var(--anim-curve);
}
.entry-banner {
width: 100%;
object-fit: cover;
transition: scale var(--duration-animation) var(--anim-curve);
}
.entry-text-container {
display: flex;
flex-direction: column;
margin: 16px 2px 2px;
/* gap: 8px; */
}
.entry-title {
font-family: var(--font-mono);
font-weight: 700;
font-size: 1.2rem;
line-height: 1.4rem;
margin: 0;
}
.entry-date {
font-size: 0.8rem;
line-height: 0.8rem;
font-weight: 600;
margin: 4px 0 0 0;
font-family: var(--font-mono);
color: var(--color-text-highlight-alt);
}
.entry-description {
font-size: 1.0rem;
line-height: 1.2rem;
margin: 4px 0 8px;
}
.entry-tag-container {
display: flex;
gap: 4px;
flex-direction: row;
flex-wrap: wrap;
}
@media screen and (max-width: 900px) {
.entry-container {
grid-template-columns: 1fr 1fr;
}
}
@media screen and (max-width: 600px) {
.entry-container {
grid-template-columns: 1fr;
}
}
</style>

View File

@@ -97,6 +97,10 @@
--color-text-secondary: #b0b0b0;
--color-text-img: invert(98%) sepia(1%) saturate(4643%) hue-rotate(297deg) brightness(115%) contrast(76%);
--color-text-dark: #1e1e1e;
--color-text-highlight: color-mix(in srgb, var(--color-highlight) 70%, var(--color-text));
--color-text-highlight-alt: color-mix(in srgb, var(--color-highlight-alt) 70%, var(--color-text));
--color-highlight: #51B86B;
--color-highlight-dark: color-mix(in srgb, var(--color-highlight) 60%, black);
--color-highlight-alt: #d03b4a;
@@ -105,8 +109,9 @@
--color-background: #111111;
--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-alt: color-mix(in srgb, var(--color-highlight-alt) 30%, transparent);
--color-background-highlight-hover: color-mix(in srgb, var(--color-highlight) 60%, transparent);
--color-background-highlight-hover-alt: color-mix(in srgb, var(--color-highlight-alt) 66%, transparent);
--color-background-highlight-hover-dark: color-mix(in srgb, var(--color-highlight-dark) 60%, transparent);
--color-waters: #242424;

View File

@@ -0,0 +1,20 @@
<script>
let { children } = $props();
</script>
{@render children()}
<style>
:global {
.post-tag {
font-family: var(--font-mono);
font-size: 0.8rem;
background-color: var(--color-background-highlight-alt);
margin: 0;
padding: 4px;
border-radius: 8px;
line-height: 1rem;
font-weight: 600;
}
}
</style>

View File

@@ -1,25 +1,24 @@
<script lang="ts">
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
import Gallery, { type GalleryEntry } from "$lib/lists/gallery.svelte";
import { posts, type BlogPostLink } from "./posts";
import { BlogPostTag, posts, type BlogPostLink } from "./posts";
import BlogGallery from "$lib/lists/blog-gallery.svelte";
let entries: GalleryEntry[] = posts.map(mapEntries);
let filter = $state(BlogPostTag.NULL);
function mapEntries(entry: BlogPostLink, index: number): GalleryEntry {
let banner = "";
if (entry.post.banner && entry.post.banner !== "") {
banner = `/blog/${entry.key}/${entry.post.banner}`;
function setFilter(tag: BlogPostTag) {
filter = tag;
console.log(filter);
}
function filterPosts(): BlogPostLink[] {
if (filter == BlogPostTag.NULL) {
return posts;
}
return {
title: `${entry.post.title}`,
subtitle: `#${(posts.length - index).toString().padStart(2, '0')} // ${entry.post.date}, ${entry.post.time}`,
img: banner,
link: `/blog/${entry.key}/`,
imgAlt: `Preview image for ${entry.post.title}`,
description: entry.post.description,
};
let a: BlogPostLink[] = posts.filter((post) => post.post.tags.includes(filter))
console.log(a);
return a;
}
</script>
@@ -33,5 +32,63 @@
banner="robert.webp"
bannerAlt="View at a tram bridge rising and then curving to the left." />
<Gallery entries={entries} />
<!-- TODO descriptions on filter click -->
<p class="tag-filter-header"># filter posts by tag:</p>
<div class="tag-filter-container">
{#each Object.values(BlogPostTag) as tag}
{#if tag == filter}
<button class="post-tag tag-filter tag-filter-selected" onclick={() => { setFilter(tag) }}>{tag}</button>
{:else}
<button class="post-tag tag-filter" onclick={() => { setFilter(tag) }}>{tag}</button>
{/if}
{/each}
</div>
<BlogGallery posts={filterPosts()} />
</Content>
<style>
.tag-filter-header {
font-family: var(--font-mono);
font-size: 0.9rem;
/* margin-left: 10px;
margin-right: 10px; */
margin: 12px 10px 4px;
color: var(--color-text-highlight-alt);
}
.tag-filter-container {
display: flex;
gap: 8px 12px;
margin: 0 10px 8px;
flex-wrap: wrap;
}
.tag-filter {
width: fit-content;
font-size: 0.9rem;
color: var(--color-text);
padding: 8px;
border-radius: 12px;
cursor: pointer;
transition: background-color var(--duration-animation) var(--anim-curve);
}
.tag-filter-selected {
background-color: var(--color-highlight-alt);
}
.tag-filter:hover {
background-color: var(--color-background-highlight-hover-alt);
}
@media screen and (max-width: 600px) {
.tag-filter-container {
gap: 8px;
}
.tag-filter {
font-size: 0.8rem;
}
}
</style>

View File

@@ -14,6 +14,8 @@ export interface BlogPostDetails {
* Description to be used in page's metadata.
*/
description: string;
tags: BlogPostTag[];
}
export interface BlogPostLink {
@@ -21,6 +23,15 @@ export interface BlogPostLink {
post: BlogPostDetails;
}
export enum BlogPostTag {
NULL = "all", // placeholder when a 'no tag' is needed. if in doubt, do not use this
ART = "art-stuff", // ramblings to do with art
DRAWING = "drawing", // self-explanatory
IMADETHIS = "i-made-this", // stuff i made
META = "natconf-meta", // about the website itself
TECH_TIP = "tech-tip", // tech guides
}
export const posts: BlogPostLink[] = [
{
@@ -32,6 +43,9 @@ export const posts: BlogPostLink[] = [
bannerAlt: "White light blurs on a darker background.",
title: "Moving On",
description: "It's time to switch domains.",
tags: [
BlogPostTag.META,
],
}
},
{
@@ -43,6 +57,9 @@ export const posts: BlogPostLink[] = [
bannerAlt: "A sunset captured from an Autobahn exit.",
title: "I made a LIGHTYEARS font",
description: "I feel electric and it's only getting brighter!",
tags: [
BlogPostTag.IMADETHIS,
],
}
},
{
@@ -54,6 +71,9 @@ export const posts: BlogPostLink[] = [
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.",
tags: [
BlogPostTag.TECH_TIP,
],
}
},
{
@@ -62,9 +82,13 @@ export const posts: BlogPostLink[] = [
date: "2026-02-14",
time: "19:46",
banner: "logins.webp",
bannerAlt: "A curved stick from a tree with some dry leaves attached. Its form resembles an entity with two legs, a spine, and no arms, leaning over and looking sad.",
bannerAlt: "A screenshot of a terminal emulator logged onto a remote server displaying log messages. The messages all display different IP addresses unsuccessfully attempting to log in with different usernames. There are over a dozen requests within a single minute on February 14, 2026.",
title: "SSH Woes",
description: "About how I was shocked to learn that my server was open for attacks for well over a year.",
tags: [
BlogPostTag.META,
BlogPostTag.TECH_TIP,
],
}
},
{
@@ -76,6 +100,9 @@ export const posts: BlogPostLink[] = [
bannerAlt: "A curved stick from a tree with some dry leaves attached. Its form resembles an entity with two legs, a spine, and no arms, leaning over and looking sad.",
title: "Am I doing too much?",
description: "I'm trying to pursue too many hobbies all at once and it's a struggle.",
tags: [
BlogPostTag.ART,
],
}
},
{
@@ -87,6 +114,11 @@ export const posts: BlogPostLink[] = [
bannerAlt: "A Leuchtturm-branded notebook with a copper-coloured cover. An eraser, a pencil sharpener, and a Faber-Castell pencil are lying on top.",
title: "Drawing Challenge",
description: "Challenging myself to draw something every day for 4 weeks.",
tags: [
BlogPostTag.ART,
BlogPostTag.DRAWING,
BlogPostTag.IMADETHIS,
],
}
},
{
@@ -98,6 +130,9 @@ export const posts: BlogPostLink[] = [
bannerAlt: "Colossus standing in the National Museum of Computing in Bletchley, UK",
title: "Lessons Learned",
description: "A small note about how you should always check whether your finished work works as intended.",
tags: [
BlogPostTag.META,
],
}
},
{
@@ -109,6 +144,11 @@ export const posts: BlogPostLink[] = [
bannerAlt: "A small drawing of an anime-style girl's head.",
title: "Limitations",
description: "Something about how boundaries can foster creativity.",
tags: [
BlogPostTag.ART,
BlogPostTag.DRAWING,
BlogPostTag.IMADETHIS,
],
}
},
];