Skip to content

Commit a2de180

Browse files
authored
Merge pull request #21 from TOM-BOHN/sandbox
Merge sandbox into main
2 parents a2f16c1 + 5b2ea06 commit a2de180

32 files changed

Lines changed: 893 additions & 331 deletions

CONTRIBUTING.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,10 @@ tom-bohn.github.io/
7171
│ ├── accredible-badge/ # Accredible badge fetcher
7272
│ └── trailhead-badge/ # Trailhead badge fetcher
7373
├── public/ # Static assets
74+
│ ├── icons/ # Site favicon and PWA icons
75+
│ ├── images/ # General images (e.g. profile photo)
7476
│ ├── badges/ # Badge images
75-
│ ├── education/ # Education images
76-
│ └── profile.jpg # Profile picture
77+
│ └── education/ # Education images
7778
└── .github/ # GitHub Actions workflows
7879
```
7980

PR_DESCRIPTION.md

Lines changed: 0 additions & 122 deletions
This file was deleted.

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,10 @@ The development server will be available at `http://localhost:3000`.
100100
│ ├── accredible-badge/ # Accredible badge fetcher
101101
│ └── trailhead-badge/ # Trailhead badge fetcher
102102
└── public/ # Static assets
103+
├── icons/ # Site favicon and PWA icons (SVG + generated PNGs)
104+
├── images/ # General images (e.g. profile photo)
103105
├── badges/ # Badge images
104-
├── education/ # Education images
105-
└── profile.jpg # Profile picture
106+
└── education/ # Education images
106107
```
107108

108109
## 🚢 Deployment
@@ -115,6 +116,15 @@ The site uses Next.js static export (`output: 'export'`) which generates a fully
115116

116117
The `CNAME` file is located in the `public/` directory. This ensures it's included in the static export and deployed to GitHub Pages, enabling the custom domain `thomaslbohn.com`.
117118

119+
## 🖼️ Icons
120+
121+
Site icons live in `public/icons/`:
122+
123+
- **icon.svg** – Source favicon (TLB on orange–blue gradient), sized so TLB stays visible when shown in a circle (e.g. in search results).
124+
- **apple-touch-icon.png**, **android-chrome-192x192.png**, **android-chrome-512x512.png** – PNGs generated from the SVG.
125+
126+
To regenerate PNGs after editing the SVG: `npm run generate:icons` (requires `sharp` as a devDependency).
127+
118128
## 📝 Content Management
119129

120130
- **Blog Posts**: Written in Markdown and stored in `content/blog/`

app/about/page.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import Image from 'next/image'
2+
import type { Metadata } from 'next'
3+
4+
export const metadata: Metadata = {
5+
title: 'About',
6+
}
27

38
export default function About() {
49
return (
@@ -35,7 +40,7 @@ export default function About() {
3540
/>
3641
<div className="relative w-full h-full rounded-lg overflow-hidden bg-bg-secondary shadow-2xl">
3742
<Image
38-
src="/profile.jpg"
43+
src="/images/profile.jpg"
3944
alt="Thomas Bohn"
4045
fill
4146
className="object-cover"

app/blog/[slug]/page.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Metadata } from 'next'
12
import { getBlogPost, getBlogPosts } from '@/lib/blog'
23
import { notFound } from 'next/navigation'
34
import Link from 'next/link'
@@ -12,6 +13,13 @@ export async function generateStaticParams() {
1213
}))
1314
}
1415

16+
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> | { slug: string } }): Promise<Metadata> {
17+
const resolvedParams = await Promise.resolve(params)
18+
const post = await getBlogPost(resolvedParams.slug)
19+
if (!post) return {}
20+
return { title: post.title }
21+
}
22+
1523
export default async function BlogPost({ params }: { params: Promise<{ slug: string }> | { slug: string } }) {
1624
const resolvedParams = await Promise.resolve(params)
1725
const post = await getBlogPost(resolvedParams.slug)

app/blog/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import Link from 'next/link'
2+
import type { Metadata } from 'next'
23
import { getBlogPosts } from '@/lib/blog'
34
import { FaMedium } from 'react-icons/fa'
45

6+
export const metadata: Metadata = {
7+
title: 'Blog',
8+
}
9+
510
export default async function Blog() {
611
const posts = await getBlogPosts()
712

app/certifications/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import type { Metadata } from 'next'
12
import { getLearningData } from '@/lib/hub'
23
import { LearningSection } from '@/components/hub/LearningSection'
34
import { LearningPageHeader } from '@/components/hub/LearningPageHeader'
45

6+
export const metadata: Metadata = {
7+
title: 'Certifications',
8+
}
9+
510
export default async function Certifications() {
611
const learningData = await getLearningData()
712

app/contact/layout.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { Metadata } from 'next'
2+
3+
export const metadata: Metadata = {
4+
title: 'Contact',
5+
}
6+
7+
export default function ContactLayout({
8+
children,
9+
}: {
10+
children: React.ReactNode
11+
}) {
12+
return children
13+
}

app/globals.css

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,10 @@ html[data-theme="xanga"] body .xanga-shell [class*="bg-bg-primary"] {
173173
background-color: var(--bg-tertiary) !important;
174174
}
175175

176-
/* Containerized boxes for Xanga mode */
176+
/* Containerized boxes for Xanga mode (exclude decorative background layers) */
177177
html[data-theme="xanga"] body main > div:not(.xanga-shell),
178178
html[data-theme="xanga"] body .xanga-shell .container > div,
179-
html[data-theme="xanga"] body .xanga-main-content > div {
179+
html[data-theme="xanga"] body .xanga-main-content > div:not(.stars-bg):not(.nebula-bg) {
180180
background-color: var(--bg-secondary);
181181
border: 2px solid var(--border-navy);
182182
padding: 20px;
@@ -192,6 +192,18 @@ html[data-theme="xanga"] body .xanga-applet {
192192
border-radius: 0 !important;
193193
}
194194

195+
/* Sidebar applets: blue outline to match main section boxes (not orange) */
196+
html[data-theme="xanga"] body .xanga-sidebar .xanga-applet,
197+
html[data-theme="xanga"] body .xanga-sidebar [class*="border"] {
198+
border-color: var(--border-navy) !important;
199+
}
200+
html[data-theme="xanga"] body .xanga-sidebar button {
201+
border-color: var(--border-navy) !important;
202+
}
203+
html[data-theme="xanga"] body .xanga-sidebar button:hover {
204+
border-color: var(--border-navy) !important;
205+
}
206+
195207
html[data-theme="xanga"] body header {
196208
background-color: var(--bg-tertiary) !important;
197209
border-bottom: 1px solid var(--border-navy) !important;
@@ -202,6 +214,39 @@ html[data-theme="xanga"] body header a {
202214
text-decoration: none !important;
203215
}
204216

217+
/* Header nav in Xanga: all links same blue by default (core + VIP, override text-accent) */
218+
html[data-theme="xanga"] body header nav a,
219+
html[data-theme="xanga"] body header nav a[class*="text-accent"] {
220+
color: var(--link) !important;
221+
}
222+
223+
/* Header nav hover in Xanga: same as light/dark (transparent, orange text) */
224+
html[data-theme="xanga"] body header nav a:hover,
225+
html[data-theme="xanga"] body header nav a:focus-visible {
226+
background-color: transparent !important;
227+
color: var(--accent-orange) !important;
228+
box-shadow: none;
229+
}
230+
html[data-theme="xanga"] body header nav a:hover *,
231+
html[data-theme="xanga"] body header nav a:focus-visible * {
232+
color: var(--accent-orange) !important;
233+
}
234+
235+
/* Light and dark: same nav hover (text color change, no box) */
236+
html[data-theme="light"] body header nav a:hover,
237+
html[data-theme="light"] body header nav a:focus-visible,
238+
html[data-theme="dark"] body header nav a:hover,
239+
html[data-theme="dark"] body header nav a:focus-visible {
240+
background-color: transparent;
241+
color: var(--accent-orange) !important;
242+
}
243+
html[data-theme="light"] body header nav a:hover *,
244+
html[data-theme="light"] body header nav a:focus-visible *,
245+
html[data-theme="dark"] body header nav a:hover *,
246+
html[data-theme="dark"] body header nav a:focus-visible * {
247+
color: var(--accent-orange) !important;
248+
}
249+
205250
/* Remove underlines from page headings (h1 with font-mono) in Xanga mode */
206251
html[data-theme="xanga"] body h1.font-mono {
207252
text-decoration: none !important;
@@ -497,7 +542,7 @@ html[data-theme="xanga"] body ::-webkit-scrollbar-thumb:hover {
497542
}
498543
}
499544

500-
/* Star Background for Dark Mode */
545+
/* Star Background for Dark Mode (above nebula so stars stay visible) */
501546
[data-theme="dark"] .stars-bg {
502547
position: fixed;
503548
inset: 0;
@@ -513,19 +558,19 @@ html[data-theme="xanga"] body ::-webkit-scrollbar-thumb:hover {
513558
background-size: 350px 200px;
514559
animation: twinkle 8s ease-in-out infinite alternate;
515560
pointer-events: none;
516-
z-index: 0;
561+
z-index: 1;
517562
}
518563

519564
@keyframes twinkle {
520565
0% {
521-
opacity: 0.3;
566+
opacity: 0.5;
522567
}
523568
100% {
524-
opacity: 0.6;
569+
opacity: 0.9;
525570
}
526571
}
527572

528-
/* Nebula Effect for Dark Mode */
573+
/* Nebula Effect for Dark Mode (behind stars) */
529574
[data-theme="dark"] .nebula-bg {
530575
position: fixed;
531576
inset: 0;

app/hub/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import type { Metadata } from 'next'
12
import { getHubSections } from '@/lib/hub'
23
import { HubPageWrapper } from '@/components/hub/HubPageWrapper'
34

5+
export const metadata: Metadata = {
6+
title: 'Hub',
7+
}
8+
49
export default async function Hub() {
510
const sections = await getHubSections()
611

0 commit comments

Comments
 (0)