diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-01-29 23:56:21 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-01-29 23:56:21 +0300 |
| commit | 35556dca71eafdac4eb5d2fe781ba39687d0b058 (patch) | |
| tree | d85489336dce549b08d385c45cec6ae0678b211b /src/lib | |
| parent | 292d9dd4241ace94bfaf50827dcedfbd40de8032 (diff) | |
initial commit
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/assets/favicon.svg | 1 | ||||
| -rw-r--r-- | src/lib/components/Footer.svelte | 9 | ||||
| -rw-r--r-- | src/lib/components/Giscus.svelte | 27 | ||||
| -rw-r--r-- | src/lib/components/Header.svelte | 27 | ||||
| -rw-r--r-- | src/lib/components/PostCard.svelte | 27 | ||||
| -rw-r--r-- | src/lib/components/ThemeToggle.svelte | 78 | ||||
| -rw-r--r-- | src/lib/index.ts | 1 | ||||
| -rw-r--r-- | src/lib/styles/global.css | 206 | ||||
| -rw-r--r-- | src/lib/utils/posts.ts | 51 |
9 files changed, 427 insertions, 0 deletions
diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
\ No newline at end of file diff --git a/src/lib/components/Footer.svelte b/src/lib/components/Footer.svelte new file mode 100644 index 0000000..f06e5a6 --- /dev/null +++ b/src/lib/components/Footer.svelte @@ -0,0 +1,9 @@ +<footer class="container"> + <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 new file mode 100644 index 0000000..96be4a4 --- /dev/null +++ b/src/lib/components/Giscus.svelte @@ -0,0 +1,27 @@ +<script lang="ts"> + import { onMount } from "svelte"; + + let container: HTMLDivElement; + + onMount(() => { + 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", "bottom"); + script.setAttribute("data-theme", "preferred_color_scheme"); + script.setAttribute("data-lang", "en"); + script.setAttribute("data-loading", "lazy"); + script.crossOrigin = "anonymous"; + script.async = true; + container.appendChild(script); + }); +</script> + +<div class="giscus-container" bind:this={container}></div> diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte new file mode 100644 index 0000000..804497e --- /dev/null +++ b/src/lib/components/Header.svelte @@ -0,0 +1,27 @@ +<script lang="ts"> + import ThemeToggle from "./ThemeToggle.svelte"; + + interface Props { + title?: string; + description?: string; + } + + 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> + + <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 new file mode 100644 index 0000000..7fb9361 --- /dev/null +++ b/src/lib/components/PostCard.svelte @@ -0,0 +1,27 @@ +<script lang="ts"> + import type { Post } from "$lib/utils/posts"; + + interface Props { + post: Post; + } + + let { post }: Props = $props(); + + 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> +</article> diff --git a/src/lib/components/ThemeToggle.svelte b/src/lib/components/ThemeToggle.svelte new file mode 100644 index 0000000..f136246 --- /dev/null +++ b/src/lib/components/ThemeToggle.svelte @@ -0,0 +1,78 @@ +<script lang="ts"> + 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"; + } + + let theme = $state<"light" | "dark">(getInitialTheme()); + + function applyTheme(newTheme: "light" | "dark") { + if (browser) { + document.documentElement.setAttribute("data-theme", newTheme); + localStorage.setItem("theme", newTheme); + } + } + + $effect(() => { + applyTheme(theme); + }); + + 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"} +> + {#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/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/src/lib/styles/global.css b/src/lib/styles/global.css new file mode 100644 index 0000000..8ec94fc --- /dev/null +++ b/src/lib/styles/global.css @@ -0,0 +1,206 @@ +*, +*::before, +*::after { + 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; +} + +[data-theme="dark"] { + --bg: #1a1a1a; + --text: #e0e0e0; + --muted: #999; + --link-hover: #fff; + --border: #333; + --code-bg: #2a2a2a; +} + +html { + -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; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin-bottom: 2rem; +} + +.container { + max-width: 900px; + margin: 0 auto; + padding: 0 2rem; +} + +p { + margin-bottom: 1rem; +} + +a { + color: var(--link); + text-decoration: none; + transition: color 0.2s; +} + +a:hover { + color: var(--link-hover); +} + +ul, +ol { + margin-bottom: 1rem; + padding-left: 1.5rem; +} + +li { + margin-bottom: 0.25rem; +} + +blockquote { + 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; +} + +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); +} + +pre code { + background: none; + padding: 0; +} + +img { + max-width: 100%; + height: auto; +} + +hr { + border: none; + border-top: 1px solid var(--border); + margin: 2rem 0; +} + +small, +time { + font-size: 15px; + color: var(--muted); +} + +/* Layout */ +.wrapper-masthead { + margin: 0 2rem 3rem; +} + +.masthead { + 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; +} + +.site-info h1 { + 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; +} + +header nav { + display: flex; + align-items: center; + gap: 1.25rem; + font-size: 18px; +} + +footer { + margin-top: 4rem; + text-align: center; +} + +article { + 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; +} + +.theme-toggle:hover { + color: var(--link-hover); +} + +.giscus-container { + 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 diff --git a/src/lib/utils/posts.ts b/src/lib/utils/posts.ts new file mode 100644 index 0000000..cc05c83 --- /dev/null +++ b/src/lib/utils/posts.ts @@ -0,0 +1,51 @@ +import type { Component } from 'svelte'; + +export interface PostMetadata { + title: string; + date: string; + description: string; + tags?: string[]; +} + +export interface Post extends PostMetadata { + slug: string; +} + +export interface PostModule { + default: Component; + metadata: PostMetadata; +} + +// Use Vite's glob import to get all .svx files from posts directory +const postFiles = import.meta.glob<PostModule>('/src/posts/*.svx', { eager: true }); + +/** + * Get all posts, sorted by date (newest first) + */ +export function getPosts(): Post[] { + const posts: Post[] = []; + + for (const path in postFiles) { + const file = postFiles[path]; + // Extract slug from path: /src/posts/hello-world.svx -> hello-world + const slug = path.split('/').pop()?.replace('.svx', '') ?? ''; + + if (file.metadata) { + posts.push({ + ...file.metadata, + slug + }); + } + } + + // Sort by date, newest first + return posts.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); +} + +/** + * Get a single post by slug + */ +export function getPost(slug: string): PostModule | undefined { + const path = `/src/posts/${slug}.svx`; + return postFiles[path]; +} |
