# Blog Design V2 Spec — Functional Luxury
**From:** Daedalus 🎨
**To:** Matt, Socrates, Wadsworth
**Date:** 2026-04-21
**Status:** Ready for implementation
**Live URL for reference:** https://notes.hoffdesk.com
---
## Executive Summary
The blog is technically sound but visually undercooked. The current aesthetic reads "MVP shipped" not "Functional Luxury." This spec delivers a prioritized roadmap to transform it from a functional document into a premium reading experience.
**Current impression:** Dark theme with spacing issues, generic typography, and no visual identity.
**Target impression:** A Roman spa for the mind — calm, intentional, luxurious in restraint.
---
## Competitive Research: What Premium Blogs Do
### The Patterns That Matter
| Technique | Paul Graham | Basecamp | Linear | Vercel | Implementation Priority |
|-----------|-------------|----------|--------|--------|------------------------|
| **Generous whitespace** | Extreme | High | Medium | Medium | **Sprint 1** |
| **Serif body text** | Yes | No | No | No | **Sprint 1** |
| **Systematic scale** | 18px base | 18px base | 16px base | 16px base | **Sprint 1** |
| **Category color coding** | No | Yes | Yes | Yes | Already implemented ✓ |
| **Dark mode default** | No | No | **Yes** | **Yes** | Already implemented ✓ |
| **Micro-interactions** | None | Minimal | **Premium** | Medium | Sprint 2 |
| **Featured imagery** | None | Minimal | **Hero shots** | **Gradients** | Sprint 2 |
| **Glassmorphism** | No | No | **Yes** | Subtle | Sprint 2 |
### Key Insights
1. **Typography is 70% of the perception** — David Bushell's 2025 redesign notes: "Atkinson Hyperlegible is the bee's knees." Premium blogs invest heavily in font choice and scale.
2. **Whitespace isn't empty — it's intentional** — The Blog Herald research: "Think of it like a conversation. You wouldn't talk to someone with your face two inches from theirs."
3. **Restrained palette wins** — "Pick two, maybe three colors max. Rainbow blogs don't scream premium. They scream amateur hour."
4. **Dark mode dominance** — Linear and Vercel both default dark. The white background era is over for developer-focused content.
---
## Top 5 Highest-Impact Visual Improvements
### 1. Typography Overhaul (Sprint 1 — HIGH)
**Problem:** Current CSS uses system fonts with no personality. The blog looks like every default Bootstrap site.
**Solution:**
- **Display/Headings:** Source Serif 4 (Google Fonts) — elegant, readable, premium feel
- **Body text:** Inter or Source Sans 3 — clean, modern, excellent at small sizes
- **Code:** 0xProto or JetBrains Mono — purpose-built for code, not just any monospace
**Specific CSS additions:**
```css
@import url('https://fonts.googleapis.com/css2?family=Source+Serif+4:opsz,wght@8..60,400;8..60,600;8..60,700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap');
:root {
--font-display: 'Source Serif 4', Georgia, serif;
--font-body: 'Inter', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', 'SF Mono', monospace;
/* Fluid type scale */
--text-hero: clamp(2rem, 5vw, 3rem);
--text-h1: clamp(1.75rem, 4vw, 2.5rem);
--text-h2: clamp(1.5rem, 3vw, 2rem);
--text-body: clamp(1rem, 1.5vw, 1.125rem);
}
.blog-article-body {
font-family: var(--font-display);
font-size: var(--text-body);
line-height: 1.75; /* Increased from 1.7 */
}
.blog-article-body h2, .blog-article-body h3 {
font-family: var(--font-body);
font-weight: 600;
}
.blog-article-body code {
font-family: var(--font-mono);
}
```
**Template change:** Add Google Fonts link to `base.html.j2` head.
**Reference:**
- https://dbushell.com/ — Atkinson Hyperlegible + 0xProto
- https://paulgraham.com/ — Verdana, generous spacing
---
### 2. Whitespace & Rhythm System (Sprint 1 — HIGH)
**Problem:** Current spacing feels cramped. The `960px` container and tight padding don't let content breathe.
**Solution:** Adopt a systematic spacing scale with more generous defaults.
```css
:root {
/* Extended space scale */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.5rem; /* 24px */
--space-6: 2rem; /* 32px */
--space-8: 3rem; /* 48px */
--space-10: 4rem; /* 64px */
--space-12: 6rem; /* 96px */
--space-16: 8rem; /* 128px */
/* Content width */
--content-max: 680px; /* Narrower for readability */
}
.blog-main {
max-width: var(--content-max);
padding: var(--space-12) var(--space-6);
}
.blog-article-body p {
margin-bottom: var(--space-6); /* Increased from space-4 */
}
.blog-article-body h2 {
margin-top: var(--space-12); /* More breathing room */
margin-bottom: var(--space-5);
}
.blog-article-body h3 {
margin-top: var(--space-10);
margin-bottom: var(--space-4);
}
.blog-article-body blockquote {
margin: var(--space-10) 0;
padding: var(--space-6) var(--space-8);
}
```
**Why this matters:** David Bushell: "I like big boring undistracted text." The current layout shifts when zoomed. This change minimizes layout shift.
---
### 3. Enhanced Dark Mode Polish (Sprint 1 — MEDIUM)
**Problem:** Dark mode works but lacks depth. No subtle gradients, no glassmorphism, flat appearance.
**Solution:** Add subtle elevation through layered backgrounds and soft borders.
```css
:root {
/* Refined color palette — still dark, but more nuanced */
--bg-primary: #0a0c10; /* Deeper black */
--bg-secondary: #111318; /* Elevated surfaces */
--bg-tertiary: #1a1d24; /* Hover states */
--bg-card: rgba(17, 19, 24, 0.8); /* For glassmorphism */
/* Subtle border colors */
--border-default: rgba(255, 255, 255, 0.08);
--border-subtle: rgba(255, 255, 255, 0.04);
--border-hover: rgba(255, 255, 255, 0.12);
/* Text with more nuance */
--text-primary: #f0f1f5;
--text-secondary: #9ca3af;
--text-tertiary: #6b7280;
/* Accent refinements */
--accent-primary: #818cf8;
--accent-glow: rgba(129, 140, 248, 0.15);
}
/* Subtle gradient overlay for hero */
.blog-hero-link {
background: linear-gradient(
145deg,
var(--bg-secondary) 0%,
rgba(17, 19, 24, 0.95) 100%
);
border: 1px solid var(--border-default);
}
/* Glassmorphism for header */
.blog-header {
background: rgba(10, 12, 16, 0.85);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border-bottom: 1px solid var(--border-default);
}
```
---
### 4. Micro-Interactions & Hover States (Sprint 2 — MEDIUM)
**Problem:** Links and cards have basic hover states. No personality, no delight.
**Solution:** Add purposeful animation that guides attention without distraction.
```css
/* Smooth transitions */
:root {
--transition-fast: 150ms ease;
--transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-slow: 350ms cubic-bezier(0.4, 0, 0.2, 1);
}
/* Card hover with lift and glow */
.blog-card {
transition: transform var(--transition-base),
box-shadow var(--transition-base),
border-color var(--transition-fast);
}
.blog-card:hover {
transform: translateY(-4px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4),
0 0 0 1px var(--border-hover);
}
/* Link hover with underline animation */
.blog-article-body a {
text-decoration: none;
background-image: linear-gradient(var(--accent-primary), var(--accent-primary));
background-size: 0% 2px;
background-position: 0% 100%;
background-repeat: no-repeat;
transition: background-size var(--transition-fast);
}
.blog-article-body a:hover {
background-size: 100% 2px;
}
/* Focus states for accessibility */
:focus-visible {
outline: 2px solid var(--accent-primary);
outline-offset: 4px;
}
/* Staggered fade-in for content */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.blog-article-header {
animation: fadeInUp 0.6s ease-out;
}
.blog-article-body > * {
animation: fadeInUp 0.6s ease-out;
animation-fill-mode: both;
}
/* Stagger children */
.blog-article-body > *:nth-child(1) { animation-delay: 0.1s; }
.blog-article-body > *:nth-child(2) { animation-delay: 0.15s; }
.blog-article-body > *:nth-child(3) { animation-delay: 0.2s; }
/* ... etc */
```
---
### 5. Featured Image System & Visual Interest (Sprint 2 — LOW)
**Problem:** No imagery. The blog is text-only which works for PG but limits visual identity.
**Solution:** Add optional featured images with gradient fallbacks for posts without images.
```css
/* Article header with optional featured image */
.blog-article-header {
position: relative;
padding: var(--space-12) 0;
margin-bottom: var(--space-10);
}
.blog-article-header.has-image {
padding: var(--space-16) 0;
margin-bottom: var(--space-12);
}
.blog-article-header-bg {
position: absolute;
inset: 0;
z-index: -1;
opacity: 0.3;
filter: saturate(0.8);
}
/* Gradient fallback when no image */
.blog-article-header-gradient {
background: linear-gradient(
135deg,
var(--cat-homelab) 0%,
var(--accent-primary) 50%,
var(--cat-ai) 100%
);
opacity: 0.1;
}
/* Category-specific gradients */
.cat-homelab .blog-article-header-gradient {
background: linear-gradient(135deg, var(--cat-homelab), #c2410c);
}
.cat-openclaw .blog-article-header-gradient {
background: linear-gradient(135deg, var(--cat-openclaw), #4f46e5);
}
.cat-ai .blog-article-header-gradient {
background: linear-gradient(135deg, var(--cat-ai), #0891b2);
}
```
**Template addition:** Add optional `featured_image` field to frontmatter, render conditionally.
---
## Sprint Breakdown
### Sprint 1: Typography & Foundation (Estimated: 2-3 hours)
**Files to modify:**
1. `blog/templates/base.html.j2` — Add Google Fonts
2. `blog/static/blog.css` — Typography system, spacing scale, color refinements
**Visual impact:** HIGH — This alone transforms the "feel" of the blog
**Testing checklist:**
- [ ] Fonts load correctly on mobile
- [ ] Content width comfortable at 320px viewport
- [ ] No layout shift on zoom (150%, 200%)
- [ ] Code blocks remain monospace
- [ ] Dark mode still works
---
### Sprint 2: Polish & Delight (Estimated: 3-4 hours)
**Files to modify:**
1. `blog/static/blog.css` — Animations, hover states, glassmorphism
2. `blog/templates/blog_article.html.j2` — Featured image support
3. Backend (Socrates) — Optional `featured_image` field in schema
**Visual impact:** MEDIUM — Adds personality and premium feel
**Testing checklist:**
- [ ] Animations respect `prefers-reduced-motion`
- [ ] Hover states work on touch devices
- [ ] No performance degradation
- [ ] Featured images render correctly with fallback
---
## Reference URLs — Blogs That Nailed It
| Blog | Why It Works | URL |
|------|--------------|-----|
| **Linear Blog** | Glassmorphism, subtle gradients, premium feel | https://linear.app/blog |
| **Vercel Blog** | Dark mode, great typography, code highlighting | https://vercel.com/blog |
| **David Bushell** | Atkinson Hyperlegible, excellent spacing | https://dbushell.com/ |
| **Paul Graham** | Extreme minimalism, readable serif | https://paulgraham.com/ |
| **Tailwind Blog** | Warmth, approachable, category colors | https://tailwindcss.com/blog |
| **37signals / Basecamp** | Bold typography, clear hierarchy | https://37signals.com/ |
---
## Specific CSS/Tailwind Additions Summary
### Fonts to Load (Google Fonts)
```html
```
### Critical CSS Variables to Add
```css
/* Typography */
--font-display: 'Source Serif 4', Georgia, serif;
--font-body: 'Inter', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
/* Fluid scale */
--text-hero: clamp(2rem, 5vw, 3rem);
--text-h1: clamp(1.75rem, 4vw, 2.5rem);
--text-body: clamp(1rem, 1.5vw, 1.125rem);
/* Extended spacing */
--space-10: 4rem;
--space-12: 6rem;
--space-16: 8rem;
--content-max: 680px;
/* Transitions */
--transition-fast: 150ms ease;
--transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1);
```
---
## Aundrea Test
> *Would Aundrea enjoy reading this at 7 AM, half-awake, on her phone?*
**Current state:** Maybe. Text is readable but feels utilitarian.
**After Sprint 1:** Yes. Serif body text feels like a real article, not a documentation page. Generous spacing lets her skim without effort.
**After Sprint 2:** Yes, and she'd notice the polish. The subtle animations and refined colors feel intentional, not accidental.
---
## Deliverables Checklist
- [x] Top 5 highest-impact improvements identified
- [x] Specific CSS/Tailwind additions documented
- [x] Reference URLs compiled
- [x] Effort estimated (Sprint 1 vs Sprint 2)
- [ ] **Next:** Matt reviews and approves
- [ ] **Next:** Socrates implements Sprint 1
- [ ] **Next:** Daedalus reviews live result
- [ ] **Next:** Sprint 2 if Sprint 1 lands well
---
*"The words are the product."* — This spec ensures the container honors the content.
— Daedalus 🎨