summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.d.ts14
-rw-r--r--src/app.html34
-rw-r--r--src/lib/components/Footer.svelte14
-rw-r--r--src/lib/components/Giscus.svelte184
-rw-r--r--src/lib/components/Header.svelte38
-rw-r--r--src/lib/components/PostCard.svelte38
-rw-r--r--src/lib/components/ThemeToggle.svelte133
-rw-r--r--src/lib/styles/global.css209
-rw-r--r--src/lib/styles/global.pruned.css153
-rw-r--r--src/lib/utils/posts.server.ts32
-rw-r--r--src/lib/utils/posts.ts6
-rw-r--r--src/routes/+layout.svelte34
-rw-r--r--src/routes/+page.server.ts10
-rw-r--r--src/routes/+page.svelte30
-rw-r--r--src/routes/+page.ts2
-rw-r--r--src/routes/about/+page.svelte30
-rw-r--r--src/routes/posts/[slug]/+page.svelte41
-rw-r--r--src/routes/posts/[slug]/+page.ts28
18 files changed, 525 insertions, 505 deletions
diff --git a/src/app.d.ts b/src/app.d.ts
index da08e6d..520c421 100644
--- a/src/app.d.ts
+++ b/src/app.d.ts
@@ -1,13 +1,13 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
- namespace App {
- // interface Error {}
- // interface Locals {}
- // interface PageData {}
- // interface PageState {}
- // interface Platform {}
- }
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface PageState {}
+ // interface Platform {}
+ }
}
export {};
diff --git a/src/app.html b/src/app.html
index 9f96d52..60ddb65 100644
--- a/src/app.html
+++ b/src/app.html
@@ -1,14 +1,28 @@
<!doctype html>
<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <script>
+ (() => {
+ try {
+ const storedTheme = localStorage.getItem("theme");
+ const theme =
+ storedTheme === "light" || storedTheme === "dark"
+ ? storedTheme
+ : window.matchMedia("(prefers-color-scheme: dark)").matches
+ ? "dark"
+ : "light";
+ document.documentElement.setAttribute("data-theme", theme);
+ } catch {
+ // Ignore read/write failures in strict privacy modes.
+ }
+ })();
+ </script>
+ %sveltekit.head%
+ </head>
-<head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- %sveltekit.head%
-</head>
-
-<body data-sveltekit-preload-data="hover">
- <div style="display: contents">%sveltekit.body%</div>
-</body>
-
+ <body data-sveltekit-preload-data="hover">
+ <div style="display: contents">%sveltekit.body%</div>
+ </body>
</html>
diff --git a/src/lib/components/Footer.svelte b/src/lib/components/Footer.svelte
index f06e5a6..e725ff9 100644
--- a/src/lib/components/Footer.svelte
+++ b/src/lib/components/Footer.svelte
@@ -1,9 +1,9 @@
<footer class="container">
- <hr />
- <p>
- <small
- >{new Date().getFullYear()}
- <a href="https://svelte.dev">SvelteKit</a></small
- >
- </p>
+ <hr />
+ <p>
+ <small
+ >{new Date().getFullYear()}
+ <a href="https://svelte.dev">SvelteKit</a></small
+ >
+ </p>
</footer>
diff --git a/src/lib/components/Giscus.svelte b/src/lib/components/Giscus.svelte
index 6659b36..3179764 100644
--- a/src/lib/components/Giscus.svelte
+++ b/src/lib/components/Giscus.svelte
@@ -1,98 +1,102 @@
<script lang="ts">
- import { onMount } from "svelte";
-
- let container: HTMLDivElement | undefined;
- let trigger: HTMLDivElement | undefined;
- let observer: IntersectionObserver | undefined;
- let isLoading = false;
- let hasLoaded = false;
-
- function loadComments() {
- if (!container || isLoading || hasLoaded) return;
-
- isLoading = true;
-
- const script = document.createElement("script");
- script.src = "https://giscus.app/client.js";
- script.setAttribute("data-repo", "wenekar/giscus");
- script.setAttribute("data-repo-id", "R_kgDOREZgEQ");
- script.setAttribute("data-category", "Announcements");
- script.setAttribute("data-category-id", "DIC_kwDOREZgEc4C1nRY");
- script.setAttribute("data-mapping", "pathname");
- script.setAttribute("data-strict", "0");
- script.setAttribute("data-reactions-enabled", "1");
- script.setAttribute("data-emit-metadata", "0");
- script.setAttribute("data-input-position", "top");
- script.setAttribute("data-theme", "preferred_color_scheme");
- script.setAttribute("data-lang", "en");
- script.setAttribute("data-loading", "lazy");
- script.crossOrigin = "anonymous";
- script.async = true;
-
- script.addEventListener("load", () => {
- isLoading = false;
- });
-
- script.addEventListener("error", () => {
- isLoading = false;
- hasLoaded = false;
- script.remove();
- });
-
- container.appendChild(script);
- hasLoaded = true;
- observer?.disconnect();
- observer = undefined;
- }
-
- onMount(() => {
- if (!trigger || !("IntersectionObserver" in window)) return;
-
- observer = new IntersectionObserver(
- (entries) => {
- if (entries.some((entry) => entry.isIntersecting)) {
- loadComments();
- }
- },
- { rootMargin: "300px 0px" },
- );
-
- observer.observe(trigger);
-
- return () => {
- observer?.disconnect();
- };
- });
+ import { onMount } from "svelte";
+
+ let container: HTMLDivElement | undefined;
+ let trigger: HTMLDivElement | undefined;
+ let observer: IntersectionObserver | undefined;
+ let isLoading = false;
+ let hasLoaded = false;
+
+ function loadComments() {
+ if (!container || isLoading || hasLoaded) return;
+
+ isLoading = true;
+
+ const script = document.createElement("script");
+ script.src = "https://giscus.app/client.js";
+ script.setAttribute("data-repo", "wenekar/giscus");
+ script.setAttribute("data-repo-id", "R_kgDOREZgEQ");
+ script.setAttribute("data-category", "Announcements");
+ script.setAttribute("data-category-id", "DIC_kwDOREZgEc4C1nRY");
+ script.setAttribute("data-mapping", "pathname");
+ script.setAttribute("data-strict", "0");
+ script.setAttribute("data-reactions-enabled", "1");
+ script.setAttribute("data-emit-metadata", "0");
+ script.setAttribute("data-input-position", "top");
+ script.setAttribute("data-theme", "preferred_color_scheme");
+ script.setAttribute("data-lang", "en");
+ script.setAttribute("data-loading", "lazy");
+ script.crossOrigin = "anonymous";
+ script.async = true;
+
+ script.addEventListener("load", () => {
+ isLoading = false;
+ });
+
+ script.addEventListener("error", () => {
+ isLoading = false;
+ hasLoaded = false;
+ script.remove();
+ });
+
+ container.appendChild(script);
+ hasLoaded = true;
+ observer?.disconnect();
+ observer = undefined;
+ }
+
+ onMount(() => {
+ if (!trigger || !("IntersectionObserver" in window)) return;
+
+ observer = new IntersectionObserver(
+ (entries) => {
+ if (entries.some((entry) => entry.isIntersecting)) {
+ loadComments();
+ }
+ },
+ { rootMargin: "300px 0px" },
+ );
+
+ observer.observe(trigger);
+
+ return () => {
+ observer?.disconnect();
+ };
+ });
</script>
<div class="giscus-container">
- {#if !hasLoaded}
- <button class="comments-toggle" on:click={loadComments} disabled={isLoading}>
- {isLoading ? "Loading comments..." : "Show comments"}
- </button>
- {/if}
-
- <div class="giscus-trigger" bind:this={trigger}></div>
- <div bind:this={container}></div>
+ {#if !hasLoaded}
+ <button
+ class="comments-toggle"
+ on:click={loadComments}
+ disabled={isLoading}
+ >
+ {isLoading ? "Loading comments..." : "Show comments"}
+ </button>
+ {/if}
+
+ <div class="giscus-trigger" bind:this={trigger}></div>
+ <div bind:this={container}></div>
</div>
<style>
- .comments-toggle {
- margin-bottom: 1rem;
- padding: 0.6rem 0.9rem;
- border: 1px solid var(--border);
- background: transparent;
- color: var(--text);
- font: inherit;
- cursor: pointer;
- }
-
- .comments-toggle:disabled {
- opacity: 0.75;
- cursor: default;
- }
-
- .giscus-trigger {
- height: 1px;
- }
+ .comments-toggle {
+ margin-bottom: 1rem;
+ padding: 0.6rem 0.9rem;
+ border: 1px solid var(--border);
+ background: transparent;
+ color: var(--text);
+ font: inherit;
+ cursor: pointer;
+ }
+
+ .comments-toggle:disabled {
+ opacity: 0.75;
+ cursor: default;
+ }
+
+ .giscus-trigger {
+ height: 1px;
+ }
</style>
diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte
index 804497e..a6708ca 100644
--- a/src/lib/components/Header.svelte
+++ b/src/lib/components/Header.svelte
@@ -1,27 +1,27 @@
<script lang="ts">
- import ThemeToggle from "./ThemeToggle.svelte";
+ import ThemeToggle from "./ThemeToggle.svelte";
- interface Props {
- title?: string;
- description?: string;
- }
+ interface Props {
+ title?: string;
+ description?: string;
+ }
- let { title = "My Blog", description = "Super. Good. Code." }: Props =
- $props();
+ let { title = "My Blog", description = "Super. Good. Code." }: Props =
+ $props();
</script>
<div class="wrapper-masthead">
- <header class="container masthead">
- <div class="site-info">
- <h1><a href="/">{title}</a></h1>
- <p class="site-description">{description}</p>
- </div>
+ <header class="container masthead">
+ <div class="site-info">
+ <h1><a href="/">{title}</a></h1>
+ <p class="site-description">{description}</p>
+ </div>
- <nav>
- <a href="/">Blog</a>
- <a href="/apps">Apps</a>
- <a href="/about">About</a>
- <ThemeToggle />
- </nav>
- </header>
+ <nav>
+ <a href="/">Blog</a>
+ <a href="/apps">Apps</a>
+ <a href="/about">About</a>
+ <ThemeToggle />
+ </nav>
+ </header>
</div>
diff --git a/src/lib/components/PostCard.svelte b/src/lib/components/PostCard.svelte
index 7fb9361..83ae71d 100644
--- a/src/lib/components/PostCard.svelte
+++ b/src/lib/components/PostCard.svelte
@@ -1,27 +1,27 @@
<script lang="ts">
- import type { Post } from "$lib/utils/posts";
+ import type { Post } from "$lib/utils/posts";
- interface Props {
- post: Post;
- }
+ interface Props {
+ post: Post;
+ }
- let { post }: Props = $props();
+ let { post }: Props = $props();
- function formatDate(dateStr: string): string {
- return new Date(dateStr).toLocaleDateString("en-US", {
- year: "numeric",
- month: "long",
- day: "numeric",
- });
- }
+ function formatDate(dateStr: string): string {
+ return new Date(dateStr).toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ });
+ }
</script>
<article>
- <header>
- <a href="/posts/{post.slug}">
- <h1 style="text-decoration: none;">{post.title}</h1>
- </a>
- <small><time datetime={post.date}>{formatDate(post.date)}</time></small>
- </header>
- <p>{post.description}</p>
+ <header>
+ <a href="/posts/{post.slug}">
+ <h1 style="text-decoration: none;">{post.title}</h1>
+ </a>
+ <small><time datetime={post.date}>{formatDate(post.date)}</time></small>
+ </header>
+ <p>{post.description}</p>
</article>
diff --git a/src/lib/components/ThemeToggle.svelte b/src/lib/components/ThemeToggle.svelte
index f136246..08f4ef5 100644
--- a/src/lib/components/ThemeToggle.svelte
+++ b/src/lib/components/ThemeToggle.svelte
@@ -1,78 +1,81 @@
<script lang="ts">
- import { browser } from "$app/environment";
+ import { browser } from "$app/environment";
- function getInitialTheme(): "light" | "dark" {
- if (browser) {
- const stored = localStorage.getItem("theme");
- if (stored === "light" || stored === "dark") return stored;
- return window.matchMedia("(prefers-color-scheme: dark)").matches
- ? "dark"
- : "light";
- }
- return "light";
+ function getInitialTheme(): "light" | "dark" {
+ if (browser) {
+ const fromRoot = document.documentElement.getAttribute("data-theme");
+ if (fromRoot === "light" || fromRoot === "dark") return fromRoot;
+
+ const stored = localStorage.getItem("theme");
+ if (stored === "light" || stored === "dark") return stored;
+ return window.matchMedia("(prefers-color-scheme: dark)").matches
+ ? "dark"
+ : "light";
}
+ return "light";
+ }
- let theme = $state<"light" | "dark">(getInitialTheme());
+ let theme = $state<"light" | "dark">(getInitialTheme());
- function applyTheme(newTheme: "light" | "dark") {
- if (browser) {
- document.documentElement.setAttribute("data-theme", newTheme);
- localStorage.setItem("theme", newTheme);
- }
+ function applyTheme(newTheme: "light" | "dark") {
+ if (browser) {
+ document.documentElement.setAttribute("data-theme", newTheme);
+ localStorage.setItem("theme", newTheme);
}
+ }
- $effect(() => {
- applyTheme(theme);
- });
+ $effect(() => {
+ applyTheme(theme);
+ });
- function toggleTheme() {
- theme = theme === "light" ? "dark" : "light";
- }
+ function toggleTheme() {
+ theme = theme === "light" ? "dark" : "light";
+ }
</script>
<button
- class="theme-toggle"
- onclick={toggleTheme}
- aria-label={theme === "light"
- ? "Switch to dark mode"
- : "Switch to light mode"}
- title={theme === "light" ? "Switch to dark mode" : "Switch to light mode"}
+ class="theme-toggle"
+ onclick={toggleTheme}
+ aria-label={theme === "light"
+ ? "Switch to dark mode"
+ : "Switch to light mode"}
+ title={theme === "light" ? "Switch to dark mode" : "Switch to light mode"}
>
- {#if theme === "light"}
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="20"
- height="20"
- viewBox="0 0 24 24"
- fill="none"
- stroke="currentColor"
- stroke-width="2"
- stroke-linecap="round"
- stroke-linejoin="round"
- >
- <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
- </svg>
- {:else}
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="20"
- height="20"
- viewBox="0 0 24 24"
- fill="none"
- stroke="currentColor"
- stroke-width="2"
- stroke-linecap="round"
- stroke-linejoin="round"
- >
- <circle cx="12" cy="12" r="5"></circle>
- <line x1="12" y1="1" x2="12" y2="3"></line>
- <line x1="12" y1="21" x2="12" y2="23"></line>
- <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
- <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
- <line x1="1" y1="12" x2="3" y2="12"></line>
- <line x1="21" y1="12" x2="23" y2="12"></line>
- <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
- <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
- </svg>
- {/if}
+ {#if theme === "light"}
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="20"
+ height="20"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ >
+ <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
+ </svg>
+ {:else}
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ width="20"
+ height="20"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ stroke-width="2"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ >
+ <circle cx="12" cy="12" r="5"></circle>
+ <line x1="12" y1="1" x2="12" y2="3"></line>
+ <line x1="12" y1="21" x2="12" y2="23"></line>
+ <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
+ <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
+ <line x1="1" y1="12" x2="3" y2="12"></line>
+ <line x1="21" y1="12" x2="23" y2="12"></line>
+ <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
+ <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
+ </svg>
+ {/if}
</button>
diff --git a/src/lib/styles/global.css b/src/lib/styles/global.css
index 8ec94fc..1ed6aca 100644
--- a/src/lib/styles/global.css
+++ b/src/lib/styles/global.css
@@ -1,42 +1,43 @@
*,
*::before,
*::after {
- box-sizing: border-box;
- margin: 0;
- padding: 0;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
}
:root {
- --bg: #fff;
- --text: #333;
- --muted: #666;
- --link: #888;
- --link-hover: #000;
- --border: #eee;
- --code-bg: #f5f5f5;
- --font: "Inter", sans-serif;
- --mono: "Bitstream Vera Sans Mono", "Courier New", monospace;
+ --bg: #fff;
+ --text: #333;
+ --muted: #666;
+ --link: #888;
+ --link-hover: #000;
+ --border: #eee;
+ --code-bg: #f5f5f5;
+ --font:
+ Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
+ "Helvetica Neue", Arial, sans-serif;
+ --mono: "Bitstream Vera Sans Mono", "Courier New", monospace;
}
[data-theme="dark"] {
- --bg: #1a1a1a;
- --text: #e0e0e0;
- --muted: #999;
- --link-hover: #fff;
- --border: #333;
- --code-bg: #2a2a2a;
+ --bg: #1a1a1a;
+ --text: #e0e0e0;
+ --muted: #999;
+ --link-hover: #fff;
+ --border: #333;
+ --code-bg: #2a2a2a;
}
html {
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
}
body {
- font: 18px/1.4 var(--font);
- color: var(--text);
- background: var(--bg);
- transition: background 0.2s, color 0.2s;
+ font: 18px/1.4 var(--font);
+ color: var(--text);
+ background: var(--bg);
}
h1,
@@ -45,162 +46,160 @@ h3,
h4,
h5,
h6 {
- margin-bottom: 2rem;
+ margin-bottom: 2rem;
}
.container {
- max-width: 900px;
- margin: 0 auto;
- padding: 0 2rem;
+ max-width: 900px;
+ margin: 0 auto;
+ padding: 0 2rem;
}
p {
- margin-bottom: 1rem;
+ margin-bottom: 1rem;
}
a {
- color: var(--link);
- text-decoration: none;
- transition: color 0.2s;
+ color: var(--link);
+ text-decoration: none;
}
a:hover {
- color: var(--link-hover);
+ color: var(--link-hover);
}
ul,
ol {
- margin-bottom: 1rem;
- padding-left: 1.5rem;
+ margin-bottom: 1rem;
+ padding-left: 1.5rem;
}
li {
- margin-bottom: 0.25rem;
+ margin-bottom: 0.25rem;
}
blockquote {
- border-left: 2px solid var(--muted);
- padding-left: 1em;
- margin: 1.5rem 0;
- font-style: italic;
+ border-left: 2px solid var(--muted);
+ padding-left: 1em;
+ margin: 1.5rem 0;
+ font-style: italic;
}
code {
- font-family: var(--mono);
- font-size: 0.9em;
- background: var(--code-bg);
- padding: 0.2em 0.4em;
- border-radius: 3px;
+ font-family: var(--mono);
+ font-size: 0.9em;
+ background: var(--code-bg);
+ padding: 0.2em 0.4em;
+ border-radius: 3px;
}
pre {
- font-family: var(--mono);
- background: var(--code-bg);
- padding: 1rem;
- border-radius: 4px;
- overflow-x: auto;
- margin: 1.5rem 0;
- box-shadow: 3px 3px rgba(0, 0, 0, 0.1);
+ font-family: var(--mono);
+ background: var(--code-bg);
+ padding: 1rem;
+ border-radius: 4px;
+ overflow-x: auto;
+ margin: 1.5rem 0;
+ box-shadow: 3px 3px rgba(0, 0, 0, 0.1);
}
pre code {
- background: none;
- padding: 0;
+ background: none;
+ padding: 0;
}
img {
- max-width: 100%;
- height: auto;
+ max-width: 100%;
+ height: auto;
}
hr {
- border: none;
- border-top: 1px solid var(--border);
- margin: 2rem 0;
+ border: none;
+ border-top: 1px solid var(--border);
+ margin: 2rem 0;
}
small,
time {
- font-size: 15px;
- color: var(--muted);
+ font-size: 15px;
+ color: var(--muted);
}
/* Layout */
.wrapper-masthead {
- margin: 0 2rem 3rem;
+ margin: 0 2rem 3rem;
}
.masthead {
- padding: 1.25rem 0;
- border-bottom: 1px solid var(--border);
- display: flex;
- justify-content: space-between;
- align-items: center;
+ padding: 1.25rem 0;
+ border-bottom: 1px solid var(--border);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
}
.site-info {
- display: flex;
- flex-direction: column;
- margin-left: 2rem;
+ display: flex;
+ flex-direction: column;
+ margin-left: 2rem;
}
.site-info h1 {
- margin: 0;
- font-size: 1.75rem;
- font-weight: 300;
- letter-spacing: 1px;
+ margin: 0;
+ font-size: 1.75rem;
+ font-weight: 300;
+ letter-spacing: 1px;
}
.site-description {
- margin: -0.25rem 0 0 0;
- color: var(--muted);
- font-size: 1rem;
+ margin: -0.25rem 0 0 0;
+ color: var(--muted);
+ font-size: 1rem;
}
header nav {
- display: flex;
- align-items: center;
- gap: 1.25rem;
- font-size: 18px;
+ display: flex;
+ align-items: center;
+ gap: 1.25rem;
+ font-size: 18px;
}
footer {
- margin-top: 4rem;
- text-align: center;
+ margin-top: 4rem;
+ text-align: center;
}
article {
- margin-bottom: 2rem;
+ margin-bottom: 2rem;
}
.theme-toggle {
- background: transparent;
- border: none;
- padding: 0.5rem;
- cursor: pointer;
- color: var(--muted);
- display: flex;
- align-items: center;
- justify-content: center;
- transition: color 0.2s;
+ background: transparent;
+ border: none;
+ padding: 0.5rem;
+ cursor: pointer;
+ color: var(--muted);
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
.theme-toggle:hover {
- color: var(--link-hover);
+ color: var(--link-hover);
}
.giscus-container {
- margin-top: 3rem;
+ margin-top: 3rem;
}
@media (max-width: 640px) {
- .masthead {
- flex-direction: column;
- text-align: center;
- gap: 1rem;
- }
-
- header nav {
- gap: 0.75rem;
- }
-} \ No newline at end of file
+ .masthead {
+ flex-direction: column;
+ text-align: center;
+ gap: 1rem;
+ }
+
+ header nav {
+ gap: 0.75rem;
+ }
+}
diff --git a/src/lib/styles/global.pruned.css b/src/lib/styles/global.pruned.css
index 45f1462..9872f39 100644
--- a/src/lib/styles/global.pruned.css
+++ b/src/lib/styles/global.pruned.css
@@ -1,154 +1,153 @@
*,
*::before,
*::after {
- box-sizing: border-box;
- margin: 0;
- padding: 0;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
}
:root {
- --bg: #fff;
- --text: #333;
- --muted: #666;
- --link: #888;
- --link-hover: #000;
- --border: #eee;
- --font: "Inter", sans-serif;
+ --bg: #fff;
+ --text: #333;
+ --muted: #666;
+ --link: #888;
+ --link-hover: #000;
+ --border: #eee;
+ --font:
+ Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
+ "Helvetica Neue", Arial, sans-serif;
}
[data-theme="dark"] {
- --bg: #1a1a1a;
- --text: #e0e0e0;
- --muted: #999;
- --link-hover: #fff;
- --border: #333;
+ --bg: #1a1a1a;
+ --text: #e0e0e0;
+ --muted: #999;
+ --link-hover: #fff;
+ --border: #333;
}
html {
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
}
body {
- font: 18px/1.4 var(--font);
- color: var(--text);
- background: var(--bg);
- transition: background 0.2s, color 0.2s;
+ font: 18px/1.4 var(--font);
+ color: var(--text);
+ background: var(--bg);
}
h1,
h2,
h3 {
- margin-bottom: 2rem;
+ margin-bottom: 2rem;
}
.container {
- max-width: 900px;
- margin: 0 auto;
- padding: 0 2rem;
+ max-width: 900px;
+ margin: 0 auto;
+ padding: 0 2rem;
}
p {
- margin-bottom: 1rem;
+ margin-bottom: 1rem;
}
a {
- color: var(--link);
- text-decoration: none;
- transition: color 0.2s;
+ color: var(--link);
+ text-decoration: none;
}
a:hover {
- color: var(--link-hover);
+ color: var(--link-hover);
}
hr {
- border: none;
- border-top: 1px solid var(--border);
- margin: 2rem 0;
+ border: none;
+ border-top: 1px solid var(--border);
+ margin: 2rem 0;
}
small,
time {
- font-size: 15px;
- color: var(--muted);
+ font-size: 15px;
+ color: var(--muted);
}
/* Layout */
.wrapper-masthead {
- margin: 0 2rem 3rem;
+ margin: 0 2rem 3rem;
}
.masthead {
- padding: 1.25rem 0;
- border-bottom: 1px solid var(--border);
- display: flex;
- justify-content: space-between;
- align-items: center;
+ padding: 1.25rem 0;
+ border-bottom: 1px solid var(--border);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
}
.site-info {
- display: flex;
- flex-direction: column;
- margin-left: 2rem;
+ display: flex;
+ flex-direction: column;
+ margin-left: 2rem;
}
.site-info h1 {
- margin: 0;
- font-size: 1.75rem;
- font-weight: 300;
- letter-spacing: 1px;
+ margin: 0;
+ font-size: 1.75rem;
+ font-weight: 300;
+ letter-spacing: 1px;
}
.site-description {
- margin: -0.25rem 0 0 0;
- color: var(--muted);
- font-size: 1rem;
+ margin: -0.25rem 0 0 0;
+ color: var(--muted);
+ font-size: 1rem;
}
header nav {
- display: flex;
- align-items: center;
- gap: 1.25rem;
+ display: flex;
+ align-items: center;
+ gap: 1.25rem;
}
footer {
- margin-top: 4rem;
- text-align: center;
+ margin-top: 4rem;
+ text-align: center;
}
article {
- margin-bottom: 2rem;
+ margin-bottom: 2rem;
}
.theme-toggle {
- background: transparent;
- border: none;
- padding: 0.5rem;
- cursor: pointer;
- color: var(--muted);
- display: flex;
- align-items: center;
- justify-content: center;
- transition: color 0.2s;
+ background: transparent;
+ border: none;
+ padding: 0.5rem;
+ cursor: pointer;
+ color: var(--muted);
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
.theme-toggle:hover {
- color: var(--link-hover);
+ color: var(--link-hover);
}
.giscus-container {
- margin-top: 3rem;
+ margin-top: 3rem;
}
@media (max-width: 640px) {
- .masthead {
- flex-direction: column;
- text-align: center;
- gap: 1rem;
- }
-
- header nav {
- gap: 0.75rem;
- }
+ .masthead {
+ flex-direction: column;
+ text-align: center;
+ gap: 1rem;
+ }
+
+ header nav {
+ gap: 0.75rem;
+ }
}
diff --git a/src/lib/utils/posts.server.ts b/src/lib/utils/posts.server.ts
index a9b7bb9..9f34c1c 100644
--- a/src/lib/utils/posts.server.ts
+++ b/src/lib/utils/posts.server.ts
@@ -1,26 +1,28 @@
-import type { Post, PostMetadata } from './posts';
+import type { Post, PostMetadata } from "./posts";
-const postMetadataFiles = import.meta.glob<PostMetadata>('/src/posts/*.svx', {
- eager: true,
- import: 'metadata'
+const postMetadataFiles = import.meta.glob<PostMetadata>("/src/posts/*.svx", {
+ eager: true,
+ import: "metadata",
});
function getSlugFromPath(path: string): string {
- return path.split('/').pop()?.replace('.svx', '') ?? '';
+ return path.split("/").pop()?.replace(".svx", "") ?? "";
}
export function getPosts(): Post[] {
- const posts: Post[] = [];
+ const posts: Post[] = [];
- for (const path in postMetadataFiles) {
- const metadata = postMetadataFiles[path];
- const slug = getSlugFromPath(path);
+ for (const path in postMetadataFiles) {
+ const metadata = postMetadataFiles[path];
+ const slug = getSlugFromPath(path);
- posts.push({
- ...metadata,
- slug
- });
- }
+ posts.push({
+ ...metadata,
+ slug,
+ });
+ }
- return posts.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
+ return posts.sort(
+ (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
+ );
}
diff --git a/src/lib/utils/posts.ts b/src/lib/utils/posts.ts
index d0eca68..ae5cf84 100644
--- a/src/lib/utils/posts.ts
+++ b/src/lib/utils/posts.ts
@@ -1,4 +1,4 @@
-import type { Component } from 'svelte';
+import type { Component } from "svelte";
export interface PostMetadata {
title: string;
@@ -16,10 +16,10 @@ export interface PostModule {
metadata: PostMetadata;
}
-const postLoaders = import.meta.glob<PostModule>('/src/posts/*.svx');
+const postLoaders = import.meta.glob<PostModule>("/src/posts/*.svx");
function getSlugFromPath(path: string): string {
- return path.split('/').pop()?.replace('.svx', '') ?? '';
+ return path.split("/").pop()?.replace(".svx", "") ?? "";
}
export function getPostSlugs(): string[] {
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 9b488bd..19a7b85 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -8,17 +8,6 @@
let { children } = $props();
onMount(() => {
- const loadFonts = () => {
- if (document.querySelector('link[data-app-fonts="true"]')) return;
-
- const link = document.createElement("link");
- link.rel = "stylesheet";
- link.href =
- "https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap";
- link.setAttribute("data-app-fonts", "true");
- document.head.appendChild(link);
- };
-
const loadAnalytics = () => {
if (document.querySelector('script[data-goatcounter-script="true"]'))
return;
@@ -32,23 +21,34 @@
document.head.appendChild(script);
};
- const runDeferredLoads = () => {
- loadFonts();
- loadAnalytics();
+ const runDeferredAnalytics = () => {
+ const idleWindow = window as Window & {
+ requestIdleCallback?: (
+ callback: IdleRequestCallback,
+ options?: IdleRequestOptions,
+ ) => number;
+ };
+
+ if (typeof idleWindow.requestIdleCallback === "function") {
+ idleWindow.requestIdleCallback(loadAnalytics, { timeout: 3000 });
+ return;
+ }
+ window.setTimeout(loadAnalytics, 1500);
};
if (document.readyState === "complete") {
- window.setTimeout(runDeferredLoads, 0);
+ runDeferredAnalytics();
return;
}
- window.addEventListener("load", runDeferredLoads, { once: true });
- return () => window.removeEventListener("load", runDeferredLoads);
+ window.addEventListener("load", runDeferredAnalytics, { once: true });
+ return () => window.removeEventListener("load", runDeferredAnalytics);
});
</script>
<svelte:head>
<link rel="icon" href={favicon} />
+ <meta name="color-scheme" content="light dark" />
</svelte:head>
<Header title="Berke Güzel" description="Super. Bad. Code." />
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts
index 8f4a3a7..c1a5530 100644
--- a/src/routes/+page.server.ts
+++ b/src/routes/+page.server.ts
@@ -1,8 +1,8 @@
-import { getPosts } from '$lib/utils/posts.server';
-import type { PageServerLoad } from './$types';
+import { getPosts } from "$lib/utils/posts.server";
+import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async () => {
- return {
- posts: getPosts()
- };
+ return {
+ posts: getPosts(),
+ };
};
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index b2c92b4..9924a19 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,24 +1,24 @@
<script lang="ts">
- import type { PageData } from "./$types";
- import PostCard from "$lib/components/PostCard.svelte";
+ import type { PageData } from "./$types";
+ import PostCard from "$lib/components/PostCard.svelte";
- let { data }: { data: PageData } = $props();
+ let { data }: { data: PageData } = $props();
</script>
<svelte:head>
- <title>wenekar sucks at programming</title>
- <meta
- name="description"
- content="A personal blog built with SvelteKit and MDsveX"
- />
+ <title>wenekar sucks at programming</title>
+ <meta
+ name="description"
+ content="A personal blog built with SvelteKit and MDsveX"
+ />
</svelte:head>
<section>
- {#if data.posts.length === 0}
- <p>No posts.</p>
- {:else}
- {#each data.posts as post}
- <PostCard {post} />
- {/each}
- {/if}
+ {#if data.posts.length === 0}
+ <p>No posts.</p>
+ {:else}
+ {#each data.posts as post}
+ <PostCard {post} />
+ {/each}
+ {/if}
</section>
diff --git a/src/routes/+page.ts b/src/routes/+page.ts
index 176ae64..189f71e 100644
--- a/src/routes/+page.ts
+++ b/src/routes/+page.ts
@@ -1 +1 @@
-export const prerender = true
+export const prerender = true;
diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte
index 3f47736..bd5b29d 100644
--- a/src/routes/about/+page.svelte
+++ b/src/routes/about/+page.svelte
@@ -1,35 +1,35 @@
<h1>About</h1>
<p>
- My name is Berke Güzel and I am a huge fan of
- <a href="https://supergoodcode.com">Super. Good. Code.</a>
+ My name is Berke Güzel and I am a huge fan of
+ <a href="https://supergoodcode.com">Super. Good. Code.</a>
</p>
<p>
- I do not do code good. I am aiming to use this page as a means of publicly
- sharing my thoughts, and
- <i>maybe</i> projects. And who knows, maybe one day, this will grow into something
- bigger than I could ever imagine.
+ I do not do code good. I am aiming to use this page as a means of publicly
+ sharing my thoughts, and
+ <i>maybe</i> projects. And who knows, maybe one day, this will grow into something
+ bigger than I could ever imagine.
</p>
<p>
- Currently I'm employed as a software "developer", working on projects that
- use Django/rest_framework stack.
+ Currently I'm employed as a software "developer", working on projects that use
+ Django/rest_framework stack.
</p>
<h2>More about me</h2>
<p>
- At present I work for a media company, they write news, also publish videos
- on YouTube. This blog shall not contain anything about the place I work at,
- or the future companies that I may get hired at.
+ At present I work for a media company, they write news, also publish videos on
+ YouTube. This blog shall not contain anything about the place I work at, or
+ the future companies that I may get hired at.
</p>
<p>
- I try avoiding social media platforms (like Facebook, Instagram etc.) like a
- plague. If anyone seems to act like me on those platforms, be certain that
- it is not me, and maybe report them to the platform but I doubt they would
- do anything anyway.
+ I try avoiding social media platforms (like Facebook, Instagram etc.) like a
+ plague. If anyone seems to act like me on those platforms, be certain that it
+ is not me, and maybe report them to the platform but I doubt they would do
+ anything anyway.
</p>
<h2>Even more!</h2>
diff --git a/src/routes/posts/[slug]/+page.svelte b/src/routes/posts/[slug]/+page.svelte
index 2a57168..dae5dff 100644
--- a/src/routes/posts/[slug]/+page.svelte
+++ b/src/routes/posts/[slug]/+page.svelte
@@ -1,34 +1,33 @@
<script lang="ts">
- import type { PageData } from "./$types";
- import Giscus from "$lib/components/Giscus.svelte";
+ import type { PageData } from "./$types";
+ import Giscus from "$lib/components/Giscus.svelte";
- let { data }: { data: PageData } = $props();
+ let { data }: { data: PageData } = $props();
- function formatDate(dateStr: string): string {
- return new Date(dateStr).toLocaleDateString("en-US", {
- year: "numeric",
- month: "long",
- day: "numeric",
- });
- }
+ function formatDate(dateStr: string): string {
+ return new Date(dateStr).toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ });
+ }
</script>
<svelte:head>
- <title>{data.metadata.title} | My Blog</title>
- <meta name="description" content={data.metadata.description} />
+ <title>{data.metadata.title} | My Blog</title>
+ <meta name="description" content={data.metadata.description} />
</svelte:head>
<article>
- <header>
- <h1>{data.metadata.title}</h1>
- <p>
- <time datetime={data.metadata.date}
- >{formatDate(data.metadata.date)}</time
- >
- </p>
- </header>
+ <header>
+ <h1>{data.metadata.title}</h1>
+ <p>
+ <time datetime={data.metadata.date}>{formatDate(data.metadata.date)}</time
+ >
+ </p>
+ </header>
- <data.content />
+ <data.content />
</article>
<Giscus />
diff --git a/src/routes/posts/[slug]/+page.ts b/src/routes/posts/[slug]/+page.ts
index 8d173ed..6535209 100644
--- a/src/routes/posts/[slug]/+page.ts
+++ b/src/routes/posts/[slug]/+page.ts
@@ -1,25 +1,25 @@
-import { getPost, getPostSlugs } from '$lib/utils/posts';
-import { error } from '@sveltejs/kit';
-import type { PageLoad } from './$types';
+import { getPost, getPostSlugs } from "$lib/utils/posts";
+import { error } from "@sveltejs/kit";
+import type { PageLoad } from "./$types";
export const prerender = true;
// Generate all post routes at build time
export function entries() {
- return getPostSlugs().map((slug) => ({ slug }));
+ return getPostSlugs().map((slug) => ({ slug }));
}
export const load: PageLoad = async ({ params }) => {
- const slug = params.slug;
- const post = await getPost(slug);
+ const slug = params.slug;
+ const post = await getPost(slug);
- if (!post) {
- error(404, `Post not found: ${slug}`);
- }
+ if (!post) {
+ error(404, `Post not found: ${slug}`);
+ }
- return {
- content: post.default,
- metadata: post.metadata,
- slug
- };
+ return {
+ content: post.default,
+ metadata: post.metadata,
+ slug,
+ };
};