Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ Site icons live in `public/icons/`:

To regenerate PNGs after editing the SVG: `npm run generate:icons` (requires `sharp` as a devDependency).

## 🖼️ Social Share Image

`public/images/og-image.png` is the Open Graph / Twitter Card image shown when a link to the site is shared. It's generated from `public/images/profile.jpg` plus the site's brand colors.

To regenerate it after updating the profile photo or brand colors: `npm run generate:og-image` (requires `sharp` as a devDependency).

## 📝 Content Management

- **Blog Posts**: Written in Markdown and stored in `content/blog/`
Expand Down
20 changes: 18 additions & 2 deletions app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Image from 'next/image'
import type { Metadata } from 'next'
import { RoleTypewriter } from '@/components/RoleTypewriter'
import { ScrollReveal } from '@/components/ScrollReveal'

export const metadata: Metadata = {
title: 'About',
Expand All @@ -16,7 +18,9 @@ export default function About() {
<div className="max-w-6xl mx-auto">
{/* Header section with role tag */}
<div className="mb-12 fade-in-up">
<p className="text-sm text-accent mb-4 font-mono typewriter-cursor">PRODUCT MANAGER & SOFTWARE DESIGNER</p>
<p className="text-sm text-accent mb-4 font-mono typewriter-cursor">
<RoleTypewriter />
</p>
<h1 className="text-6xl md:text-7xl font-bold mb-8">
<span className="gradient-text">
Thomas Bohn
Expand Down Expand Up @@ -54,7 +58,8 @@ export default function About() {
</div>

{/* Right column - About content */}
<div className="prose prose-lg max-w-none fade-in-up fade-in-up-delay-2">
<div className="prose prose-lg max-w-none">
<ScrollReveal>
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-6 text-text-primary font-mono flex items-center gap-3">
<span className="text-accent-orange">{'⟩'}</span>
Expand Down Expand Up @@ -108,7 +113,9 @@ export default function About() {
</p>
</div>
</section>
</ScrollReveal>

<ScrollReveal>
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-6 text-text-primary font-mono flex items-center gap-3">
<span className="text-accent-orange">{'⟩'}</span>
Expand Down Expand Up @@ -145,7 +152,9 @@ export default function About() {
</p>
</div>
</section>
</ScrollReveal>

<ScrollReveal>
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-6 text-text-primary font-mono flex items-center gap-3">
<span className="text-accent-orange">{'⟩'}</span>
Expand Down Expand Up @@ -182,7 +191,9 @@ export default function About() {
</p>
</div>
</section>
</ScrollReveal>

<ScrollReveal>
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-6 text-text-primary font-mono flex items-center gap-3">
<span className="text-accent-orange">{'⟩'}</span>
Expand Down Expand Up @@ -235,7 +246,9 @@ export default function About() {
</p>
</div>
</section>
</ScrollReveal>

<ScrollReveal>
<section className="mb-10">
<h2 className="text-2xl font-semibold mb-6 text-text-primary font-mono flex items-center gap-3">
<span className="text-accent-orange">{'⟩'}</span>
Expand Down Expand Up @@ -270,7 +283,9 @@ export default function About() {
</p>
</div>
</section>
</ScrollReveal>

<ScrollReveal>
<section>
<h2 className="text-2xl font-semibold mb-4 text-text-primary font-mono flex items-center gap-3">
<span className="text-accent-orange">{'⟩'}</span>
Expand All @@ -291,6 +306,7 @@ export default function About() {
Contact Me
</a>
</section>
</ScrollReveal>
</div>
</div>
</div>
Expand Down
22 changes: 15 additions & 7 deletions app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { getBlogPost, getBlogPosts } from '@/lib/blog'
import { notFound } from 'next/navigation'
import Link from 'next/link'
import { FaMedium } from 'react-icons/fa'
import { ReadingProgressBar } from '@/components/blog/ReadingProgressBar'
import { ShareButton } from '@/components/blog/ShareButton'

export const dynamicParams = false

Expand Down Expand Up @@ -30,10 +32,11 @@ export default async function BlogPost({ params }: { params: Promise<{ slug: str

return (
<div className="container mx-auto px-4 py-12">
<ReadingProgressBar />
<div className="max-w-6xl mx-auto">
<div className="mb-8">
<p className="text-sm text-accent mb-4 font-mono">{'>'} SOFTWARE ENGINEER</p>
<div className="flex items-center justify-between mb-6">
<div className="flex items-center justify-between mb-6 flex-wrap gap-3">
<h1 className="text-2xl font-semibold text-text-primary font-mono">
{'// BLOG'}
</h1>
Expand All @@ -44,6 +47,7 @@ export default async function BlogPost({ params }: { params: Promise<{ slug: str
>
← Back to Blog
</Link>
<ShareButton title={post.title} />
{post.mediumUrl && (
<a
href={post.mediumUrl}
Expand All @@ -61,12 +65,16 @@ export default async function BlogPost({ params }: { params: Promise<{ slug: str

<article className="prose prose-lg max-w-6xl">
<div className="mb-4">
<p className="text-text-secondary text-sm">
{new Date(post.date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
<p className="text-text-secondary text-sm flex items-center gap-2">
<span>
{new Date(post.date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</span>
<span aria-hidden="true">&middot;</span>
<span>{post.readingTimeMinutes} min read</span>
</p>
</div>
{post.title.includes(':') ? (
Expand Down
29 changes: 19 additions & 10 deletions app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import Link from 'next/link'
import type { Metadata } from 'next'
import { getBlogPosts } from '@/lib/blog'
import { FaMedium } from 'react-icons/fa'
import { RoleTypewriter } from '@/components/RoleTypewriter'
import { ScrollReveal } from '@/components/ScrollReveal'

export const metadata: Metadata = {
title: 'Blog',
Expand All @@ -19,7 +21,9 @@ export default async function Blog() {
<div className="container mx-auto px-4 py-12 relative z-10">
<div className="max-w-6xl mx-auto">
<div className="mb-12 fade-in-up">
<p className="text-sm text-accent mb-4 font-mono typewriter-cursor">PRODUCT MANAGER & SOFTWARE DESIGNER</p>
<p className="text-sm text-accent mb-4 font-mono typewriter-cursor">
<RoleTypewriter />
</p>
<h1 className="text-2xl font-semibold mb-4 text-text-primary font-mono flex items-center gap-3">
<span className="text-accent-orange">{'⟩'}</span>
{'BLOG'}
Expand All @@ -37,10 +41,10 @@ export default async function Blog() {
</p>
</div>
) : (
<div className="space-y-8 fade-in-up fade-in-up-delay-1">
{posts.map((post) => (
<div className="space-y-8">
{posts.map((post, index) => (
<ScrollReveal key={post.slug} delayMs={Math.min(index, 4) * 80}>
<article
key={post.slug}
className="glass-card shine-effect rounded-xl p-6 hover:border-accent transition-all"
>
<div className="flex items-start justify-between gap-4 mb-2">
Expand Down Expand Up @@ -73,12 +77,16 @@ export default async function Blog() {
</a>
)}
</div>
<p className="text-text-secondary text-sm mb-4">
{new Date(post.date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
<p className="text-text-secondary text-sm mb-4 flex items-center gap-2">
<span>
{new Date(post.date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</span>
<span aria-hidden="true">&middot;</span>
<span>{post.readingTimeMinutes} min read</span>
</p>
<p className="text-text-secondary leading-loose">
{post.excerpt}
Expand All @@ -93,6 +101,7 @@ export default async function Blog() {
</svg>
</Link>
</article>
</ScrollReveal>
))}
</div>
)}
Expand Down
10 changes: 7 additions & 3 deletions app/certifications/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { Metadata } from 'next'
import { getLearningData } from '@/lib/hub'
import { LearningSection } from '@/components/hub/LearningSection'
import { LearningPageHeader } from '@/components/hub/LearningPageHeader'
import { RoleTypewriter } from '@/components/RoleTypewriter'
import { ScrollReveal } from '@/components/ScrollReveal'

export const metadata: Metadata = {
title: 'Certifications',
Expand All @@ -19,7 +21,9 @@ export default async function Certifications() {
<div className="container mx-auto px-4 py-12 relative z-10">
<div className="max-w-6xl mx-auto">
<div className="mb-12 fade-in-up">
<p className="text-sm text-accent mb-4 font-mono typewriter-cursor">PRODUCT MANAGER & SOFTWARE DESIGNER</p>
<p className="text-sm text-accent mb-4 font-mono typewriter-cursor">
<RoleTypewriter />
</p>
<div className="flex items-center justify-between mb-4">
<h1 className="text-2xl font-semibold text-text-primary font-mono flex items-center gap-3">
<span className="text-accent-orange">{'⟩'}</span>
Expand All @@ -32,13 +36,13 @@ export default async function Certifications() {
</p>
</div>

<div className="fade-in-up fade-in-up-delay-1">
<ScrollReveal>
<LearningSection
education={learningData.education}
accomplished={learningData.accomplished}
planning={learningData.planning}
/>
</div>
</ScrollReveal>
</div>
</div>
</>
Expand Down
16 changes: 13 additions & 3 deletions app/contact/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import { useState } from 'react'
import { FaLinkedin, FaEnvelope, FaCopy, FaCheck, FaLock } from 'react-icons/fa'
import { RoleTypewriter } from '@/components/RoleTypewriter'
import { ScrollReveal } from '@/components/ScrollReveal'

export default function Contact() {
const [copied, setCopied] = useState(false)
Expand All @@ -27,18 +29,21 @@ export default function Contact() {
<div className="container mx-auto px-4 py-12 relative z-10">
<div className="max-w-6xl mx-auto">
<div className="mb-12 fade-in-up">
<p className="text-sm text-accent mb-4 font-mono typewriter-cursor">PRODUCT MANAGER & SOFTWARE DESIGNER</p>
<p className="text-sm text-accent mb-4 font-mono typewriter-cursor">
<RoleTypewriter />
</p>
<h1 className="text-2xl font-semibold mb-4 text-text-primary font-mono flex items-center gap-3">
<span className="text-accent-orange">{'⟩'}</span>
{'CONTACT'}
</h1>
<p className="text-text-secondary leading-loose text-lg">
I'm always open to discussing new opportunities, technical challenges, or potential collaborations. Reach out through any of the channels below.
I&apos;m always open to discussing new opportunities, technical challenges, or potential collaborations. Reach out through any of the channels below.
</p>
</div>

<div className="space-y-6 fade-in-up fade-in-up-delay-1">
<div className="space-y-6">
{/* Email Section */}
<ScrollReveal>
<div className="glass-card shine-effect rounded-xl p-6">
<div className="flex items-center gap-4 mb-4">
<div className="w-12 h-12 bg-gradient-to-br from-accent to-accent-hover rounded-xl flex items-center justify-center icon-bounce shadow-lg">
Expand Down Expand Up @@ -72,8 +77,10 @@ export default function Contact() {
</svg>
</a>
</div>
</ScrollReveal>

{/* LinkedIn Section */}
<ScrollReveal delayMs={100}>
<div className="glass-card shine-effect rounded-xl p-6">
<div className="flex items-center gap-4 mb-4">
<div className="w-12 h-12 bg-gradient-to-br from-[#0077b5] to-[#005582] rounded-xl flex items-center justify-center icon-bounce shadow-lg">
Expand All @@ -97,8 +104,10 @@ export default function Contact() {
</svg>
</a>
</div>
</ScrollReveal>

{/* Other Section */}
<ScrollReveal delayMs={200}>
<div className="glass-card rounded-xl p-6 border-l-4 border-l-accent-orange">
<h2 className="text-2xl font-semibold mb-4 text-text-primary">More Ways to Connect</h2>
<p className="text-text-secondary mb-4">
Expand All @@ -118,6 +127,7 @@ export default function Contact() {
</p>
</div>
</div>
</ScrollReveal>
</div>
</div>
</div>
Expand Down
21 changes: 19 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Header } from '@/components/Header'
import { Footer } from '@/components/Footer'
import { XangaShell } from '@/components/XangaShell'
import { XangaLayoutWrapper } from '@/components/XangaLayoutWrapper'
import { BackToTop } from '@/components/BackToTop'

const openSans = Open_Sans({
subsets: ['latin'],
Expand Down Expand Up @@ -36,11 +37,20 @@ export const metadata: Metadata = {
url: 'https://thomaslbohn.com',
siteName: 'Thomas Bohn',
type: 'website',
images: [
{
url: '/images/og-image.png',
width: 1200,
height: 630,
alt: 'Thomas Bohn - Product Manager & Software Designer',
},
],
},
twitter: {
card: 'summary',
card: 'summary_large_image',
title: 'Thomas Bohn',
description: 'Personal website and blog',
images: ['/images/og-image.png'],
},
}

Expand Down Expand Up @@ -84,14 +94,21 @@ export default function RootLayout({
<meta name="referrer" content="strict-origin-when-cross-origin" />
</head>
<body className={`${openSans.className} ${openSans.variable}`} suppressHydrationWarning>
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 focus:z-[100] focus:rounded focus:bg-accent focus:px-4 focus:py-2 focus:text-white focus:shadow-lg"
>
Skip to content
</a>
<ThemeProvider>
<XangaLayoutWrapper>
<div className="min-h-screen flex flex-col">
<Header />
<main className="flex-grow">
<main id="main-content" className="flex-grow">
<XangaShell>{children}</XangaShell>
</main>
<Footer />
<BackToTop />
</div>
</XangaLayoutWrapper>
</ThemeProvider>
Expand Down
Loading
Loading