init blog

This commit is contained in:
2026-01-12 14:12:30 +01:00
parent cd69b0ac26
commit 38d0e7e0a0
22 changed files with 7126 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
import React from 'react';
interface BlockquoteProps {
children: React.ReactNode;
className?: string;
}
export const Blockquote: React.FC<BlockquoteProps> = ({ children, className = '' }) => (
<blockquote className={`border-l-4 border-slate-400 pl-4 italic text-slate-600 my-6 ${className}`}>
{children}
</blockquote>
);
export const CodeBlock: React.FC<{ children: React.ReactNode; className?: string }> = ({ children, className = '' }) => (
<pre className={`bg-slate-900 text-slate-100 p-4 rounded-lg overflow-x-auto mb-4 ${className}`}>
<code className="font-mono text-sm">
{children}
</code>
</pre>
);
export const InlineCode: React.FC<{ children: React.ReactNode; className?: string }> = ({ children, className = '' }) => (
<code className={`bg-slate-200 text-slate-900 px-1 py-0.5 rounded font-mono text-sm ${className}`}>
{children}
</code>
);

View File

@@ -0,0 +1,24 @@
import React from 'react';
interface HeadingProps {
children: React.ReactNode;
className?: string;
}
export const H1: React.FC<HeadingProps> = ({ children, className = '' }) => (
<h1 className={`text-4xl font-bold text-slate-900 mb-4 mt-8 leading-tight ${className}`}>
{children}
</h1>
);
export const H2: React.FC<HeadingProps> = ({ children, className = '' }) => (
<h2 className={`text-3xl font-bold text-slate-900 mb-4 mt-8 leading-snug ${className}`}>
{children}
</h2>
);
export const H3: React.FC<HeadingProps> = ({ children, className = '' }) => (
<h3 className={`text-2xl font-bold text-slate-900 mb-4 mt-8 leading-relaxed ${className}`}>
{children}
</h3>
);

View File

@@ -0,0 +1,24 @@
import React from 'react';
interface ListProps {
children: React.ReactNode;
className?: string;
}
export const UL: React.FC<ListProps> = ({ children, className = '' }) => (
<ul className={`list-disc list-inside text-slate-700 leading-relaxed mb-4 ml-4 ${className}`}>
{children}
</ul>
);
export const OL: React.FC<ListProps> = ({ children, className = '' }) => (
<ol className={`list-decimal list-inside text-slate-700 leading-relaxed mb-4 ml-4 ${className}`}>
{children}
</ol>
);
export const LI: React.FC<ListProps> = ({ children, className = '' }) => (
<li className={`mb-1 ${className}`}>
{children}
</li>
);

View File

@@ -0,0 +1,18 @@
import React from 'react';
interface ParagraphProps {
children: React.ReactNode;
className?: string;
}
export const Paragraph: React.FC<ParagraphProps> = ({ children, className = '' }) => (
<p className={`text-slate-700 leading-relaxed mb-4 ${className}`}>
{children}
</p>
);
export const LeadParagraph: React.FC<ParagraphProps> = ({ children, className = '' }) => (
<p className={`text-xl text-slate-700 leading-relaxed mb-6 ${className}`}>
{children}
</p>
);

View File

@@ -0,0 +1,48 @@
import React from 'react';
interface BlogPostCardProps {
title: string;
description: string;
date: string;
slug: string;
tags?: string[];
}
export const BlogPostCard: React.FC<BlogPostCardProps> = ({
title,
description,
date,
slug,
tags = []
}) => {
const formattedDate = new Date(date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
return (
<article className="border border-slate-200 rounded-lg p-6 hover:shadow-lg transition-shadow">
<a href={`/blog/${slug}`} className="block">
<h2 className="text-2xl font-bold text-slate-900 mb-2 hover:text-slate-700">
{title}
</h2>
<p className="text-slate-600 mb-3 leading-relaxed">
{description}
</p>
<div className="flex items-center justify-between text-sm text-slate-500">
<time dateTime={date}>{formattedDate}</time>
{tags.length > 0 && (
<div className="flex gap-2">
{tags.map(tag => (
<span key={tag} className="bg-slate-100 text-slate-700 px-2 py-1 rounded">
{tag}
</span>
))}
</div>
)}
</div>
</a>
</article>
);
};

View File

@@ -0,0 +1,23 @@
import React from 'react';
interface ContainerProps {
children: React.ReactNode;
className?: string;
maxWidth?: string;
}
export const Container: React.FC<ContainerProps> = ({
children,
className = '',
maxWidth = 'max-w-4xl'
}) => (
<div className={`mx-auto px-4 ${maxWidth} ${className}`}>
{children}
</div>
);
export const ArticleContainer: React.FC<ContainerProps> = ({ children, className = '' }) => (
<Container maxWidth="max-w-3xl" className={`py-8 ${className}`}>
{children}
</Container>
);

24
src/data/blogPosts.ts Normal file
View File

@@ -0,0 +1,24 @@
export interface BlogPost {
title: string;
description: string;
date: string;
slug: string;
tags: string[];
}
export const blogPosts: BlogPost[] = [
{
title: "Starting this blog",
description: "Why I'm writing things down in public",
date: "2024-01-15",
slug: "first-note",
tags: ["meta", "learning"]
},
{
title: "Debugging with print statements",
description: "When printf debugging is actually the right tool",
date: "2024-01-20",
slug: "debugging-tips",
tags: ["debugging", "tools"]
}
];

View File

@@ -0,0 +1,100 @@
---
interface Props {
title: string;
description?: string;
}
const { title, description = "Technical problem solver's blog - practical insights and learning notes" } = Astro.props;
// About info from context
const aboutInfo = {
name: "Marc Mintel",
role: "Technical problem solver",
email: "marc@mintel.me",
location: "Vulkaneifel, Germany"
};
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title} | {aboutInfo.name}</title>
<meta name="description" content={description} />
<meta name="generator" content={Astro.generator} />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body class="bg-white text-slate-900 font-sans antialiased">
<div class="min-h-screen flex flex-col">
<!-- Header with About Info -->
<header class="bg-white border-b border-slate-200 sticky top-0 z-10">
<div class="max-w-4xl mx-auto px-4 py-4">
<div class="flex items-center justify-between">
<div>
<h1 class="text-xl font-bold text-slate-900">
<a href="/" class="hover:text-slate-700">{aboutInfo.name}</a>
</h1>
<p class="text-sm text-slate-600">{aboutInfo.role}</p>
</div>
<div class="text-right text-sm text-slate-600 hidden sm:block">
<p>{aboutInfo.email}</p>
<p>{aboutInfo.location}</p>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="flex-grow">
<slot />
</main>
<!-- Footer -->
<footer class="border-t border-slate-200 bg-slate-50">
<div class="max-w-4xl mx-auto px-4 py-6">
<p class="text-sm text-slate-600 text-center">
A public notebook of things I figured out, mistakes I made, and tools I tested.
</p>
</div>
</footer>
</div>
<style is:global>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
}
a {
color: #2563eb;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}
/* Focus styles for accessibility */
a:focus,
button:focus {
outline: 2px solid #2563eb;
outline-offset: 2px;
}
</style>
</body>
</html>

View File

@@ -0,0 +1,133 @@
---
import type { CollectionEntry } from 'astro:content';
import BaseLayout from './BaseLayout.astro';
interface Props {
post: CollectionEntry<'blog'>;
}
const { post } = Astro.props;
const { title, description, date, tags } = post.data;
const formattedDate = new Date(date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
---
<BaseLayout title={title} description={description}>
<article class="max-w-3xl mx-auto px-4 py-8">
<header class="mb-8">
<h1 class="text-4xl font-bold text-slate-900 mb-4 leading-tight">
{title}
</h1>
<div class="flex items-center justify-between text-sm text-slate-500 mb-4">
<time datetime={date}>{formattedDate}</time>
{tags && tags.length > 0 && (
<div class="flex gap-2">
{tags.map(tag => (
<span key={tag} class="bg-slate-100 text-slate-700 px-2 py-1 rounded text-xs">
{tag}
</span>
))}
</div>
)}
</div>
<p class="text-xl text-slate-600 leading-relaxed">
{description}
</p>
</header>
<div class="prose prose-slate max-w-none">
<slot />
</div>
</article>
</BaseLayout>
<style>
.prose {
line-height: 1.75;
}
.prose h2 {
font-size: 1.875rem;
font-weight: 700;
margin-top: 2rem;
margin-bottom: 1rem;
color: #0f172a;
line-height: 1.3;
}
.prose h3 {
font-size: 1.5rem;
font-weight: 600;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
color: #0f172a;
line-height: 1.4;
}
.prose p {
margin-bottom: 1.25rem;
color: #334155;
}
.prose ul, .prose ol {
margin-bottom: 1.25rem;
padding-left: 1.5rem;
}
.prose li {
margin-bottom: 0.5rem;
color: #334155;
}
.prose ul li {
list-style-type: disc;
}
.prose ol li {
list-style-type: decimal;
}
.prose blockquote {
border-left: 4px solid #94a3b8;
padding-left: 1rem;
font-style: italic;
color: #475569;
margin: 1.5rem 0;
}
.prose code {
background-color: #f1f5f9;
color: #0f172a;
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
font-size: 0.875em;
font-family: 'Courier New', monospace;
}
.prose pre {
background-color: #0f172a;
color: #f1f5f9;
padding: 1rem;
border-radius: 0.5rem;
overflow-x: auto;
margin-bottom: 1.25rem;
}
.prose pre code {
background: none;
color: inherit;
padding: 0;
}
.prose a {
color: #2563eb;
text-decoration: underline;
}
.prose a:hover {
color: #1d4ed8;
}
</style>

View File

@@ -0,0 +1,79 @@
---
import BlogLayout from '../../layouts/BlogLayout.astro';
import { H2, H3 } from '../../components/ArticleHeading';
import { Paragraph, LeadParagraph } from '../../components/ArticleParagraph';
import { UL, LI } from '../../components/ArticleList';
import { CodeBlock, InlineCode } from '../../components/ArticleBlockquote';
const post = {
data: {
title: "Debugging with print statements",
description: "When printf debugging is actually the right tool",
date: new Date("2024-01-20"),
tags: ["debugging", "tools"]
}
};
---
<BlogLayout post={post}>
<LeadParagraph>
Sometimes the simplest debugging tool is the best one. Print statements get a bad reputation, but they're often exactly what you need.
</LeadParagraph>
<H2>Why print statements work</H2>
<Paragraph>
Debuggers are powerful, but they change how your code runs. Print statements don't. They let you see what's actually happening in the real execution flow.
</Paragraph>
<H3>When to use them</H3>
<UL>
<LI>Quick investigation of unexpected behavior</LI>
<LI>Understanding data flow through multiple functions</LI>
<LI>Checking state at specific points in time</LI>
<LI>When setting up a debugger feels like overkill</LI>
</UL>
<H2>Make them useful</H2>
<Paragraph>
Bad print statements create noise. Good ones tell you exactly what you need to know.
</Paragraph>
<CodeBlock>
{`def process_data(data):
# Bad: What does this even mean?
print("debug 1")
# Good: Clear context and value
print(f"Processing {len(data)} items")
result = expensive_operation(data)
# Good: Show the important intermediate result
print(f"Operation result: {result}")
return result`}
</CodeBlock>
<H3>What to print</H3>
<UL>
<LI>Variable values at key points</LI>
<LI>Function entry/exit with parameters</LI>
<LI>Loop iterations with counters</LI>
<LI>Conditional branches taken</LI>
<LI>Timing information for performance</LI>
</UL>
<H2>Temporary by design</H2>
<Paragraph>
The beauty of print statements is that they're temporary. Add them, get your answer, remove them. No setup, no cleanup, no commitment.
</Paragraph>
<Paragraph>
Sometimes the most sophisticated tool is the one you can use in 5 seconds and throw away in 10.
</Paragraph>
</BlogLayout>

View File

@@ -0,0 +1,69 @@
---
import BlogLayout from '../../layouts/BlogLayout.astro';
import { H2, H3 } from '../../components/ArticleHeading';
import { Paragraph, LeadParagraph } from '../../components/ArticleParagraph';
import { UL, LI } from '../../components/ArticleList';
import { Blockquote, InlineCode } from '../../components/ArticleBlockquote';
const post = {
data: {
title: "Starting this blog",
description: "Why I'm writing things down in public",
date: new Date("2024-01-15"),
tags: ["meta", "learning"]
}
};
---
<BlogLayout post={post}>
<LeadParagraph>
This blog is a public notebook. It's where I document things I learn, problems I solve, and tools I test.
</LeadParagraph>
<H2>Why write in public?</H2>
<Paragraph>
I forget things. Writing them down helps. Making them public helps me think more clearly and might help someone else.
</Paragraph>
<Paragraph>
The goal isn't to teach or impress. It's to document. If you find something useful here, great. If not, that's fine too.
</Paragraph>
<H2>What to expect</H2>
<UL>
<LI>Short entries, usually under 500 words</LI>
<LI>Practical solutions to specific problems</LI>
<LI>Notes on tools and workflows</LI>
<LI>Mistakes and what I learned</LI>
<LI>Occasional deep dives when needed</LI>
</UL>
<H2>How I work</H2>
<Paragraph>
My process is simple:
</Paragraph>
<UL>
<LI>I try things</LI>
<LI>I break things</LI>
<LI>I fix things</LI>
<LI>I write down what I learned</LI>
</UL>
<Blockquote>
Understanding doesn't expire. Finished projects do.
</Blockquote>
<H3>Tools I use</H3>
<Paragraph>
Mostly standard Unix tools, Python, shell scripts, and whatever gets the job done. I prefer <InlineCode>simple</InlineCode> over <InlineCode>clever</InlineCode>.
</Paragraph>
<Paragraph>
If you're building things and solving problems, maybe you'll find something useful here. If not, thanks for reading anyway.
</Paragraph>
</BlogLayout>

49
src/pages/index.astro Normal file
View File

@@ -0,0 +1,49 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import { BlogPostCard } from '../components/BlogPostCard';
import { blogPosts } from '../data/blogPosts';
// Sort posts by date
const posts = [...blogPosts].sort((a, b) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
);
---
<BaseLayout title="Blog" description="Technical problem solving blog - practical insights and learning notes">
<div class="max-w-4xl mx-auto px-4 py-8">
<!-- About Section -->
<section class="mb-12 pb-8 border-b border-slate-200">
<p class="text-lg text-slate-700 leading-relaxed">
I work on technical problems and build tools, scripts, and systems to solve them.
Sometimes that means code, sometimes automation, sometimes AI, sometimes something else.
The tool is secondary. The problem comes first.
</p>
<p class="text-sm text-slate-500 mt-4">
Topics: Vibe coding with AI • Debugging • Mac tools • Automation • Small scripts • Learning notes • FOSS
</p>
</section>
<!-- Blog Posts -->
<section>
<h2 class="text-2xl font-bold text-slate-900 mb-6">Recent Notes</h2>
{posts.length === 0 ? (
<div class="text-center py-12">
<p class="text-slate-500">No posts yet. Check back soon!</p>
</div>
) : (
<div class="space-y-4">
{posts.map(post => (
<BlogPostCard
title={post.title}
description={post.description}
date={post.date}
slug={post.slug}
tags={post.tags}
/>
))}
</div>
)}
</section>
</div>
</BaseLayout>