Blog/temp/appshell.html

1486 lines
55 KiB
HTML

<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Caleb's Blog</title>
<style>
/* Catppuccin Latte - Light theme */
html[data-theme="latte"] {
--bg-primary: #eff1f5;
--bg-secondary: #e6e9f0;
--text-primary: #4c4f69;
--text-secondary: #6c6f85;
--border-color: #dce0e8;
}
/* Catppuccin Frappe - Light-dark theme */
html[data-theme="frappe"] {
--bg-primary: #292c3c;
--bg-secondary: #414559;
--text-primary: #c6d0f5;
--text-secondary: #a5adce;
--border-color: #51576d;
}
/* Catppuccin Macchiato - Dark theme */
html[data-theme="macchiato"] {
--bg-primary: #24273a;
--bg-secondary: #363a4f;
--text-primary: #cad1f5;
--text-secondary: #b8c5e4;
--border-color: #49506a;
}
/* Catppuccin Mocha - Darkest theme */
html[data-theme="mocha"] {
--bg-primary: #1e1e2e;
--bg-secondary: #313244;
--text-primary: #cdd6f4;
--text-secondary: #bac2de;
--border-color: #45475a;
}
/* Nord - Arctic, north-bluish color scheme */
html[data-theme="nord"] {
--bg-primary: #2e3440;
--bg-secondary: #3b4252;
--text-primary: #eceff4;
--text-secondary: #d8dee9;
--border-color: #434c5e;
}
/* Dracula - Dark theme with vibrant colors */
html[data-theme="dracula"] {
--bg-primary: #282a36;
--bg-secondary: #363944;
--text-primary: #f8f8f2;
--text-secondary: #e6db74;
--border-color: #44475a;
}
/* Solarized Light - Classic light warm theme */
html[data-theme="solarized-light"] {
--bg-primary: #fdf6e3;
--bg-secondary: #eee8d5;
--text-primary: #657b83;
--text-secondary: #586e75;
--border-color: #d6d0c8;
}
/* Solarized Dark - Classic dark warm theme */
html[data-theme="solarized-dark"] {
--bg-primary: #002b36;
--bg-secondary: #073642;
--text-primary: #839496;
--text-secondary: #93a1a1;
--border-color: #586e75;
}
/* Gruvbox Light - Retro groove light */
html[data-theme="gruvbox-light"] {
--bg-primary: #fbf1c7;
--bg-secondary: #f2e5bc;
--text-primary: #3c3836;
--text-secondary: #504945;
--border-color: #d5c4a1;
}
/* Gruvbox Dark - Retro groove dark */
html[data-theme="gruvbox-dark"] {
--bg-primary: #282828;
--bg-secondary: #3c3836;
--text-primary: #ebdbb2;
--text-secondary: #d5c4a1;
--border-color: #504945;
}
/* One Dark - Popular dark theme */
html[data-theme="one-dark"] {
--bg-primary: #282c34;
--bg-secondary: #353b45;
--text-primary: #abb2bf;
--text-secondary: #a0a9b8;
--border-color: #3e4451;
}
/* Tokyo Night - Modern dark theme */
html[data-theme="tokyo-night"] {
--bg-primary: #1a1b26;
--bg-secondary: #16161e;
--text-primary: #c0caf5;
--text-secondary: #a9b1d6;
--border-color: #2f3549;
}
/* Fallback light theme */
html[data-theme="light"] {
--bg-primary: #ffffff;
--bg-secondary: #f5f5f5;
--text-primary: #000000;
--text-secondary: #333333;
--border-color: #e0e0e0;
}
/* Fallback dark theme */
html[data-theme="dark"] {
--bg-primary: #1a1a1a;
--bg-secondary: #2a2a2a;
--text-primary: #ffffff;
--text-secondary: #cccccc;
--border-color: #444444;
}
body {
margin: 0;
padding: 0;
overflow-x: hidden;
background-color: var(--bg-primary);
color: var(--text-primary);
transition: background-color 0.3s ease, color 0.3s ease;
display: flex;
flex-direction: row;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
width: 100vw;
}
main {
flex: 1 1 auto;
max-width: 900px;
min-width: 0;
overflow-y: auto;
padding: 40px 20px;
background-color: var(--bg-primary);
}
aside {
display: flex;
flex-direction: column;
gap: 15px;
padding: 20px;
width: 340px;
flex-shrink: 0;
position: sticky;
top: 0;
max-height: 100vh;
overflow-y: auto;
}
.themePicker label {
font-size: 14px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.theme-controls {
display: flex;
flex-direction: column;
gap: 8px;
}
/* Mode toggle */
.theme-mode-toggle {
display: flex;
gap: 4px;
}
.mode-btn {
flex: 1;
padding: 6px 8px;
border: 2px solid var(--border-color);
border-radius: 4px;
background-color: transparent;
color: var(--text-primary);
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
cursor: pointer;
transition: all 0.2s ease;
}
.mode-btn:hover {
border-color: var(--text-primary);
}
.mode-btn.active {
border-color: var(--text-primary);
background-color: var(--text-primary);
color: var(--bg-primary);
}
/* Custom dropdown trigger */
.theme-dropdown-trigger {
padding: 6px 8px;
border: 2px solid var(--border-color);
border-radius: 4px;
background-color: var(--bg-primary);
color: var(--text-primary);
font-size: 14px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
.theme-dropdown-trigger:hover {
border-color: var(--text-primary);
}
.theme-dropdown-trigger:focus {
outline: none;
border-color: var(--text-primary);
box-shadow: 0 0 0 3px rgba(100, 100, 100, 0.1);
}
.dropdown-arrow {
font-size: 12px;
transition: transform 0.2s ease;
margin-left: 8px;
flex-shrink: 0;
}
.theme-dropdown-trigger.open .dropdown-arrow {
transform: rotate(180deg);
}
/* Custom dropdown menu */
.theme-dropdown-menu {
position: absolute;
top: 100%;
left: 0;
right: 0;
background-color: var(--bg-primary);
border: 2px solid var(--border-color);
border-radius: 6px;
margin-top: 4px;
padding: 12px;
display: none;
flex-direction: column;
z-index: 1000;
max-height: 400px;
overflow-y: auto;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.theme-dropdown-menu.open {
display: flex;
}
.theme-dropdown-wrapper {
position: relative;
width: 100%;
}
/* Theme option row */
.theme-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px;
border-radius: 4px;
transition: background-color 0.2s ease;
cursor: pointer;
gap: 8px;
}
.theme-option:hover {
background-color: var(--bg-secondary);
}
.theme-name {
font-size: 13px;
font-weight: 500;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
flex: 1;
}
/* Selection button */
.theme-selector-btn {
width: 24px;
height: 24px;
border-radius: 4px;
border: 2px solid var(--border-color);
background-color: transparent;
cursor: pointer;
transition: all 0.2s ease;
position: relative;
flex-shrink: 0;
}
.theme-selector-btn:hover {
border-color: var(--text-primary);
}
.theme-selector-btn.selected {
border-color: var(--text-primary);
background-color: var(--text-primary);
}
.theme-selector-btn.selected::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 12px;
color: var(--bg-primary);
font-weight: bold;
}
.profile-header {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
margin-bottom: 20px;
text-align: center;
}
.profile-picture {
margin: 0;
width: 90px;
height: 90px;
border-radius: 50%;
object-fit: cover;
border: 3px solid var(--border-color);
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.profile-picture:hover {
transform: scale(1.05);
border-color: var(--text-primary);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.profile-name {
font-size: 24px;
font-weight: 700;
margin: 0;
padding: 0;
letter-spacing: -0.5px;
line-height: 1.2;
color: var(--text-primary);
}
.social-links {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 12px;
margin: 0;
padding: 0;
list-style: none;
}
.social-links li {
margin: 0;
}
.social-links a {
display: flex;
align-items: center;
justify-content: center;
width: 42px;
height: 42px;
border-radius: 8px;
background-color: var(--bg-primary);
border: 2px solid var(--border-color);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.social-links #teabag {
fill: var(--bg-primary);
}
.social-links a::before {
content: '';
position: absolute;
inset: 0;
background-color: var(--text-primary);
opacity: 0;
transition: opacity 0.3s ease;
}
.social-links a:hover {
border-color: var(--text-primary);
text-decoration: none;
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}
.social-links a:hover::before {
opacity: 0.05;
}
.social-links a:active {
transform: translateY(0);
}
.social-links svg {
width: 22px;
height: 22px;
fill: var(--text-primary);
transition: all 0.3s ease;
position: relative;
z-index: 1;
}
.social-links a:hover svg:not(.icon-github):not(.icon-gitea) {
transform: scale(1.1);
}
.git-icon-wrapper {
position: relative;
width: 24px;
height: 24px;
overflow: hidden;
}
.icon-github,
.icon-gitea {
position: absolute;
width: 24px;
height: 24px;
top: 0;
left: 0;
fill: var(--text-primary);
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.icon-github {
transform: translateY(0);
}
.icon-gitea {
transform: translateY(-44px);
}
.social-links a:hover .icon-github {
transform: translateY(48px);
}
.social-links a:hover .icon-gitea {
transform: translateY(-20px);
}
ul {
margin: 0;
padding-left: 16px;
}
li {
margin-bottom: 6px;
}
a {
color: inherit;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
h1 {
margin-top: 0;
}
.hidden {
display: none;
}
.sheet-background {
padding: 15px;
border-radius: 8px;
background-color: var(--bg-secondary);
transition: background-color 0.3s ease;
}
.tags h3 {
margin-top: 0;
margin-bottom: 12px;
font-size: 16px;
font-weight: 600;
}
.tag-pills {
display: flex;
flex-wrap: wrap;
gap: 4px;
list-style: none;
padding: 0;
margin: 0;
}
.tag-pills li {
margin: 0;
}
.tag-pill {
display: inline-block;
padding: 4px 12px;
border-radius: 16px;
background-color: var(--bg-primary);
border: 1px solid var(--border-color);
color: var(--text-primary);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
white-space: nowrap;
text-decoration: none;
}
.tag-pill:hover {
text-decoration: underline;
}
.tag-pill.active {
background-color: var(--text-primary);
color: var(--bg-primary);
border-color: var(--text-primary);
}
/* Post Archive Styles */
.postList h3 {
margin-top: 0;
margin-bottom: 12px;
font-size: 16px;
font-weight: 600;
}
.post-archive {
list-style: none;
padding: 0;
margin: 0;
}
.post-archive > li {
margin-bottom: 8px;
}
.archive-year,
.archive-month {
cursor: pointer;
padding: 6px 8px;
border-radius: 4px;
transition: background-color 0.2s ease;
display: flex;
align-items: center;
gap: 6px;
user-select: none;
}
.archive-year:hover,
.archive-month:hover {
background-color: var(--bg-primary);
}
.archive-year {
font-weight: 600;
font-size: 14px;
}
.archive-month {
font-weight: 500;
font-size: 13px;
padding-left: 20px;
}
.archive-toggle {
font-size: 10px;
transition: transform 0.2s ease;
flex-shrink: 0;
}
.archive-toggle.expanded {
transform: rotate(90deg);
}
.archive-content {
overflow: hidden;
max-height: 0;
transition: max-height 0.3s ease;
}
.archive-content.expanded {
max-height: 2000px;
}
.archive-months {
list-style: none;
padding: 0;
margin: 4px 0 0 0;
}
.archive-posts {
list-style: none;
padding: 0;
margin: 4px 0 0 0;
}
.archive-post {
padding: 4px 8px 4px 40px;
margin-bottom: 2px;
}
.archive-post a {
font-size: 13px;
color: var(--text-secondary);
transition: color 0.2s ease;
}
.archive-post a:hover {
color: var(--text-primary);
}
.post-count {
font-size: 12px;
color: var(--text-secondary);
margin-left: auto;
}
/* Blog Post Styles */
.blog-post {
max-width: 750px;
margin: 0 auto;
padding: 40px 20px;
}
.post-header {
margin-bottom: 48px;
padding-bottom: 24px;
border-bottom: 1px solid var(--border-color);
}
.post-title {
font-size: 42px;
font-weight: 700;
line-height: 1.2;
margin: 0 0 16px 0;
color: var(--text-primary);
letter-spacing: -0.02em;
}
.post-meta {
display: flex;
align-items: center;
gap: 8px;
font-size: 15px;
color: var(--text-secondary);
margin-bottom: 16px;
}
.meta-separator {
opacity: 0.5;
}
.post-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.post-tag {
display: inline-block;
padding: 4px 12px;
border-radius: 16px;
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
color: var(--text-primary);
font-size: 13px;
font-weight: 500;
text-decoration: none;
transition: all 0.2s ease;
}
.post-tag:hover {
background-color: var(--text-primary);
color: var(--bg-primary);
border-color: var(--text-primary);
text-decoration: none;
}
/* Post Content Typography */
.post-content {
font-size: 18px;
line-height: 1.75;
color: var(--text-primary);
}
.post-content .lead {
font-size: 22px;
line-height: 1.6;
margin-bottom: 32px;
color: var(--text-primary);
font-weight: 400;
}
.post-content h2 {
font-size: 32px;
font-weight: 700;
line-height: 1.3;
margin: 48px 0 24px 0;
color: var(--text-primary);
letter-spacing: -0.01em;
}
.post-content h3 {
font-size: 24px;
font-weight: 600;
line-height: 1.4;
margin: 36px 0 20px 0;
color: var(--text-primary);
}
.post-content p {
margin: 0 0 24px 0;
}
.post-content ul,
.post-content ol {
margin: 0 0 24px 0;
padding-left: 24px;
}
.post-content li {
margin-bottom: 12px;
line-height: 1.75;
}
.post-content li:last-child {
margin-bottom: 0;
}
.post-content strong {
font-weight: 600;
color: var(--text-primary);
}
/* Inline Code */
.post-content code {
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: 0.9em;
padding: 2px 6px;
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-primary);
}
/* Code Blocks */
.post-content pre {
margin: 32px 0;
padding: 20px;
background-color: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
overflow-x: auto;
line-height: 1.6;
}
.post-content pre code {
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
font-size: 15px;
padding: 0;
background-color: transparent;
border: none;
border-radius: 0;
display: block;
color: var(--text-primary);
}
/* Blockquotes */
.post-content blockquote {
margin: 32px 0;
padding: 20px 24px;
border-left: 4px solid var(--text-primary);
background-color: var(--bg-secondary);
font-style: italic;
font-size: 20px;
line-height: 1.6;
color: var(--text-secondary);
}
.post-content blockquote p {
margin: 0;
}
/* Links in content */
.post-content a {
color: var(--text-primary);
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 2px;
transition: opacity 0.2s ease;
}
.post-content a:hover {
opacity: 0.7;
}
/* Post Footer */
.post-footer {
margin-top: 48px;
padding-top: 24px;
border-top: 1px solid var(--border-color);
}
.post-footer p {
font-style: italic;
color: var(--text-secondary);
margin: 0;
}
/* Responsive adjustments */
@media (max-width: 1200px) {
aside {
width: 300px;
}
}
@media (max-width: 1024px) {
body {
flex-direction: column-reverse;
}
main {
max-width: 100%;
width: 100%;
padding: 30px 20px;
}
aside {
width: auto;
max-width: 100%;
position: static;
max-height: none;
border-left: none;
border-top: 1px solid var(--border-color);
padding: 20px;
margin: auto;
}
.blog-post {
max-width: 750px;
margin: 0 auto;
}
}
@media (max-width: 768px) {
main {
padding: 20px 16px;
}
aside {
padding: 20px 16px;
}
.blog-post {
padding: 20px 0;
}
.post-title {
font-size: 32px;
}
.post-content {
font-size: 17px;
}
.post-content .lead {
font-size: 20px;
}
.post-content h2 {
font-size: 26px;
margin: 36px 0 20px 0;
}
.post-content h3 {
font-size: 21px;
margin: 28px 0 16px 0;
}
.post-content blockquote {
font-size: 18px;
padding: 16px 20px;
}
.post-content pre {
padding: 16px;
margin: 24px -16px;
border-radius: 0;
}
.profile-picture {
width: 80px;
height: 80px;
}
.profile-name {
font-size: 20px;
}
.social-links a {
width: 38px;
height: 38px;
}
.social-links svg {
width: 20px;
height: 20px;
}
}
@media (max-width: 480px) {
.post-title {
font-size: 28px;
}
.post-content h2 {
font-size: 24px;
}
.post-content h3 {
font-size: 20px;
}
.tag-pills {
gap: 6px;
}
.tag-pill {
font-size: 12px;
padding: 4px 10px;
}
}
</style>
</head>
<body>
<main>
<article class="blog-post">
<header class="post-header">
<h1 class="post-title">Building a Modern Blog with TypeScript and Bun</h1>
<div class="post-meta">
<time datetime="2025-10-20">October 20, 2025</time>
<span class="meta-separator"></span>
<span class="read-time">8 min read</span>
</div>
</header>
<div class="post-content">
<p class="lead">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. In this article, we'll explore how to build a modern, performant blog using TypeScript and Bun.
</p>
<h2>Why TypeScript?</h2>
<p>
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. TypeScript provides type safety and better developer experience, making it easier to catch errors early and maintain large codebases.
</p>
<blockquote>
"TypeScript is JavaScript with syntax for types. It's a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale."
</blockquote>
<h2>Getting Started with Bun</h2>
<p>
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Bun is an all-in-one JavaScript runtime and toolkit designed for speed, complete with a bundler, test runner, and Node.js-compatible package manager.
</p>
<h3>Installation</h3>
<p>First, let's install Bun on your system:</p>
<pre><code class="language-bash">curl -fsSL https://bun.sh/install | bash</code></pre>
<p>Once installed, you can verify the installation by checking the version:</p>
<pre><code class="language-bash">bun --version</code></pre>
<h3>Creating a New Project</h3>
<p>
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Here's how to set up a basic TypeScript project with Bun:
</p>
<pre><code class="language-typescript">// index.ts
import { serve } from "bun";
const server = serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/") {
return new Response("Hello, World!", {
headers: { "Content-Type": "text/plain" },
});
}
return new Response("Not Found", { status: 404 });
},
});
console.log(`Server running at http://localhost:${server.port}`);</code></pre>
<h2>Key Features</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Here are some of the standout features:</p>
<ul>
<li><strong>Lightning-fast performance:</strong> Bun is significantly faster than Node.js for many operations</li>
<li><strong>Built-in TypeScript support:</strong> No need for additional transpilation steps</li>
<li><strong>Hot reloading:</strong> Automatic reloading during development with the <code>--watch</code> flag</li>
<li><strong>Modern APIs:</strong> Native support for Web APIs like <code>fetch</code> and <code>WebSocket</code></li>
</ul>
<h3>Configuration Example</h3>
<p>Here's a sample <code>tsconfig.json</code> for your project:</p>
<pre><code class="language-json">{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"lib": ["ESNext"],
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"types": ["bun-types"]
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}</code></pre>
<h2>Performance Comparison</h2>
<p>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium. Let's look at a simple benchmark:
</p>
<pre><code class="language-typescript">// benchmark.ts
const iterations = 1_000_000;
console.time("Array creation");
const arr = new Array(iterations);
for (let i = 0; i < iterations; i++) {
arr[i] = i * 2;
}
console.timeEnd("Array creation");
console.time("Array processing");
const result = arr.map(x => x * 3).filter(x => x % 2 === 0);
console.timeEnd("Array processing");
console.log(`Processed ${result.length} items`);</code></pre>
<h2>Conclusion</h2>
<p>
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Bun and TypeScript make a powerful combination for building modern web applications.
</p>
<p>
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit. With Bun's speed and TypeScript's type safety, you can build robust applications faster than ever before.
</p>
<div class="post-footer">
<p>Thanks for reading! Feel free to reach out with questions or comments.</p>
</div>
</div>
</article>
</main>
<aside>
<div class="themePicker sheet-background">
<label for="theme" class="hidden">Theme</label>
<div class="theme-controls">
<div class="theme-mode-toggle">
<button class="mode-btn active" id="lightModeBtn" data-mode="light">Light</button>
<button class="mode-btn" id="darkModeBtn" data-mode="dark">Dark</button>
</div>
<div class="theme-dropdown-wrapper">
<button class="theme-dropdown-trigger" id="themeDropdownTrigger">
<span id="currentThemeDisplay">Catppuccin Latte</span>
<span class="dropdown-arrow"></span>
</button>
<div class="theme-dropdown-menu" id="themeDropdownMenu">
<!-- Theme options will be populated by JavaScript -->
</div>
</div>
</div>
</div>
<div class="aboutMe sheet-background">
<div class="profile-header">
<img src="../src/public/profile-picture.webp" alt="Caleb Braaten" class="profile-picture">
<h3 class="profile-name">Caleb Braaten</h3>
</div>
<ul class="social-links">
<li>
<a href="https://linkedin.com/in/your-profile" target="_blank" aria-label="LinkedIn">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
</svg>
</a>
</li>
<li>
<a href="https://x.com/your-handle" target="_blank" aria-label="X (Twitter)">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
</svg>
</a>
</li>
<li>
<a href="https://github.com/your-username" target="_blank" aria-label="GitHub / Gitea">
<div class="git-icon-wrapper">
<svg class="icon-github" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
</svg>
<svg class="icon-gitea" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640">
<g>
<path id="teabag" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8
c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4
c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
<g>
<g>
<path d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2
c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5
c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5
c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3
c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1
C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4
c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7
S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55
c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8
l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"/>
<path d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4
c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1
c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9
c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3
c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3
c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29
c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8
C343.2,346.5,335,363.3,326.8,380.1z"/>
</g>
</g>
</g>
</svg>
</div>
</a>
</li>
</ul>
</div>
<div class="tags sheet-background">
<h3>Tags</h3>
<ul class="tag-pills">
<li><a href="/?tag=web-development" class="tag-pill active">Web Development</a></li>
<li><a href="/?tag=ui-ux-design" class="tag-pill">UI/UX Design</a></li>
<li><a href="/?tag=productivity" class="tag-pill">Productivity</a></li>
<li><a href="/?tag=career" class="tag-pill active">Career</a></li>
<li><a href="/?tag=javascript" class="tag-pill">JavaScript</a></li>
<li><a href="/?tag=typescript" class="tag-pill">TypeScript</a></li>
</ul>
</div>
<div class="postList sheet-background">
<h3>Posts</h3>
<ul class="post-archive">
<!-- 2025 -->
<li>
<div class="archive-year">
<span class="archive-toggle"></span>
<span>2025</span>
<span class="post-count">(8)</span>
</div>
<div class="archive-content">
<ul class="archive-months">
<li>
<div class="archive-month">
<span class="archive-toggle"></span>
<span>October</span>
<span class="post-count">(3)</span>
</div>
<div class="archive-content">
<ul class="archive-posts">
<li class="archive-post"><a href="/blog/post-1">Building a Modern Blog</a></li>
<li class="archive-post"><a href="/blog/post-2">TypeScript Tips & Tricks</a></li>
<li class="archive-post"><a href="/blog/post-3">Designing Better UIs</a></li>
</ul>
</div>
</li>
<li>
<div class="archive-month">
<span class="archive-toggle"></span>
<span>September</span>
<span class="post-count">(5)</span>
</div>
<div class="archive-content">
<ul class="archive-posts">
<li class="archive-post"><a href="/blog/post-4">Getting Started with Bun</a></li>
<li class="archive-post"><a href="/blog/post-5">Web Performance Optimization</a></li>
<li class="archive-post"><a href="/blog/post-6">CSS Grid vs Flexbox</a></li>
<li class="archive-post"><a href="/blog/post-7">JavaScript Best Practices</a></li>
<li class="archive-post"><a href="/blog/post-8">Accessibility Matters</a></li>
</ul>
</div>
</li>
</ul>
</div>
</li>
<!-- 2024 -->
<li>
<div class="archive-year">
<span class="archive-toggle"></span>
<span>2024</span>
<span class="post-count">(12)</span>
</div>
<div class="archive-content">
<ul class="archive-months">
<li>
<div class="archive-month">
<span class="archive-toggle"></span>
<span>December</span>
<span class="post-count">(4)</span>
</div>
<div class="archive-content">
<ul class="archive-posts">
<li class="archive-post"><a href="/blog/post-9">Year in Review 2024</a></li>
<li class="archive-post"><a href="/blog/post-10">Holiday Coding Projects</a></li>
<li class="archive-post"><a href="/blog/post-11">New Year Resolutions</a></li>
<li class="archive-post"><a href="/blog/post-12">Reflecting on Growth</a></li>
</ul>
</div>
</li>
<li>
<div class="archive-month">
<span class="archive-toggle"></span>
<span>June</span>
<span class="post-count">(8)</span>
</div>
<div class="archive-content">
<ul class="archive-posts">
<li class="archive-post"><a href="/blog/post-13">Summer Tech Trends</a></li>
<li class="archive-post"><a href="/blog/post-14">Remote Work Tips</a></li>
<li class="archive-post"><a href="/blog/post-15">Learning Resources</a></li>
<li class="archive-post"><a href="/blog/post-16">Code Review Best Practices</a></li>
<li class="archive-post"><a href="/blog/post-17">Git Workflow Tips</a></li>
<li class="archive-post"><a href="/blog/post-18">Docker for Beginners</a></li>
<li class="archive-post"><a href="/blog/post-19">API Design Patterns</a></li>
<li class="archive-post"><a href="/blog/post-20">Testing Strategies</a></li>
</ul>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
</aside>
<script>
const LIGHT_THEMES = ['latte', 'solarized-light', 'gruvbox-light'];
const DARK_THEMES = ['frappe', 'macchiato', 'mocha', 'solarized-dark', 'gruvbox-dark', 'nord', 'dracula', 'one-dark', 'tokyo-night'];
const THEME_NAMES = {
latte: 'Catppuccin Latte',
frappe: 'Catppuccin Frappe',
macchiato: 'Catppuccin Macchiato',
mocha: 'Catppuccin Mocha',
'solarized-light': 'Solarized Light',
'solarized-dark': 'Solarized Dark',
'gruvbox-light': 'Gruvbox Light',
'gruvbox-dark': 'Gruvbox Dark',
nord: 'Nord',
dracula: 'Dracula',
'one-dark': 'One Dark',
'tokyo-night': 'Tokyo Night'
};
const THEMES = {
light: 'latte',
dark: 'mocha'
};
let currentMode = 'light';
let isPreviewActive = false;
// Initialize theme preferences from localStorage
function initializeTheme() {
const savedLightTheme = localStorage.getItem('theme-light') || 'latte';
const savedDarkTheme = localStorage.getItem('theme-dark') || 'mocha';
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
THEMES.light = savedLightTheme;
THEMES.dark = savedDarkTheme;
currentMode = prefersDark ? 'dark' : 'light';
updateModeToggle();
updateThemeDropdown();
applyTheme();
}
// Apply the appropriate theme
function applyTheme() {
const theme = THEMES[currentMode];
document.documentElement.setAttribute('data-theme', theme);
updateCurrentThemeDisplay();
}
// Update theme dropdown based on current mode
function updateThemeDropdown() {
const menu = document.getElementById('themeDropdownMenu');
const themes = currentMode === 'light' ? LIGHT_THEMES : DARK_THEMES;
menu.innerHTML = themes.map(theme => `
<div class="theme-option" data-theme="${theme}">
<div class="theme-name">${THEME_NAMES[theme]}</div>
<button class="theme-selector-btn" data-theme="${theme}"></button>
</div>
`).join('');
// Re-attach event listeners
attachThemeOptionListeners();
updateUI();
}
// Attach event listeners to theme options
function attachThemeOptionListeners() {
document.querySelectorAll('.theme-option').forEach(option => {
// Hover for preview
option.addEventListener('mouseenter', () => {
const theme = option.getAttribute('data-theme');
previewTheme(theme);
});
option.addEventListener('mouseleave', () => {
revertPreview();
});
// Click anywhere to select
option.addEventListener('click', (e) => {
const theme = option.getAttribute('data-theme');
selectTheme(theme);
});
});
}
// Select a theme
function selectTheme(theme) {
THEMES[currentMode] = theme;
localStorage.setItem(`theme-${currentMode}`, theme);
updateUI();
applyTheme();
}
// Preview a theme temporarily
function previewTheme(theme) {
isPreviewActive = true;
document.documentElement.setAttribute('data-theme', theme);
}
// Revert from preview to actual theme
function revertPreview() {
isPreviewActive = false;
applyTheme();
}
// Update the display text for current theme
function updateCurrentThemeDisplay() {
const theme = THEMES[currentMode];
const themeName = THEME_NAMES[theme];
document.getElementById('currentThemeDisplay').textContent = themeName;
}
// Update UI to show selected theme
function updateUI() {
const currentTheme = THEMES[currentMode];
document.querySelectorAll('.theme-selector-btn').forEach(btn => {
const theme = btn.getAttribute('data-theme');
btn.classList.toggle('selected', currentTheme === theme);
});
}
// Update mode toggle buttons
function updateModeToggle() {
document.getElementById('lightModeBtn').classList.toggle('active', currentMode === 'light');
document.getElementById('darkModeBtn').classList.toggle('active', currentMode === 'dark');
}
// Handle mode button clicks
document.getElementById('lightModeBtn').addEventListener('click', () => {
currentMode = 'light';
localStorage.setItem('theme-mode', 'light');
updateModeToggle();
updateThemeDropdown();
applyTheme();
});
document.getElementById('darkModeBtn').addEventListener('click', () => {
currentMode = 'dark';
localStorage.setItem('theme-mode', 'dark');
updateModeToggle();
updateThemeDropdown();
applyTheme();
});
// Handle dropdown open/close
const trigger = document.getElementById('themeDropdownTrigger');
const menu = document.getElementById('themeDropdownMenu');
trigger.addEventListener('click', () => {
trigger.classList.toggle('open');
menu.classList.toggle('open');
});
// Close dropdown when clicking outside
document.addEventListener('click', (e) => {
if (!e.target.closest('.theme-dropdown-wrapper')) {
trigger.classList.remove('open');
menu.classList.remove('open');
revertPreview();
}
});
// Listen for system theme changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (localStorage.getItem('theme-mode') !== 'light' && localStorage.getItem('theme-mode') !== 'dark') {
currentMode = e.matches ? 'dark' : 'light';
updateModeToggle();
updateThemeDropdown();
applyTheme();
}
});
// Initialize on page load
initializeTheme();
// Post Archive Toggle Functionality
document.addEventListener('DOMContentLoaded', () => {
// Handle year and month toggles
document.querySelectorAll('.archive-year, .archive-month').forEach(toggle => {
toggle.addEventListener('click', (e) => {
e.stopPropagation();
const toggleIcon = toggle.querySelector('.archive-toggle');
const content = toggle.nextElementSibling;
// Toggle expanded state
toggleIcon.classList.toggle('expanded');
content.classList.toggle('expanded');
});
});
});
</script>
</body>
</html>