Skip to content

Commit 3032396

Browse files
authored
Add Minecraft game-style template (#33)
1 parent 01949f6 commit 3032396

24 files changed

+2013
-13
lines changed

package-lock.json

Lines changed: 862 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App.tsx

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ function App() {
3838
const isNetflix = template === 'netflix'
3939
// threejs template gets sci-fi HUD navbar
4040
const isThreejs = template === 'threejs'
41+
// minecraft template gets game UI styling
42+
const isMinecraft = template === 'minecraft'
4143

4244
const showSearch = (githubConfig as { showSearch?: boolean }).showSearch !== false
4345

@@ -172,25 +174,31 @@ function App() {
172174
? 'min-h-screen bg-[#141414] text-white'
173175
: isThreejs
174176
? 'min-h-screen bg-[#050509] text-slate-50'
175-
: 'min-h-screen bg-slate-50 text-slate-900 dark:bg-[#050509] dark:text-slate-50'
177+
: isMinecraft
178+
? 'min-h-screen bg-[#1a1a2e] text-white'
179+
: 'min-h-screen bg-slate-50 text-slate-900 dark:bg-[#050509] dark:text-slate-50'
176180
const headerClasses = isHacker
177181
? 'sticky top-0 z-20 border-b border-green-900/60 bg-black/95 backdrop-blur font-mono'
178182
: isNetflix
179183
? 'sticky top-0 z-20 border-b border-white/5 bg-[#141414]/95 backdrop-blur'
180184
: isThreejs
181185
? 'sticky top-0 z-20 bg-[#050509]/80 backdrop-blur-md'
182-
: theme === 'dark'
183-
? 'sticky top-0 z-20 border-b border-white/5 bg-[#050509]/90 backdrop-blur'
184-
: 'sticky top-0 z-20 border-b border-slate-200 bg-slate-50/90 backdrop-blur'
186+
: isMinecraft
187+
? 'sticky top-0 z-20 border-b-2 border-[#3b3b3b] bg-[#8b8b8b]/95 backdrop-blur'
188+
: theme === 'dark'
189+
? 'sticky top-0 z-20 border-b border-white/5 bg-[#050509]/90 backdrop-blur'
190+
: 'sticky top-0 z-20 border-b border-slate-200 bg-slate-50/90 backdrop-blur'
185191
const footerClasses = isHacker
186192
? 'border-t border-green-900/60 bg-black py-4 text-xs text-green-900 font-mono'
187193
: isNetflix
188194
? 'border-t border-white/5 bg-[#141414] py-6 text-xs text-[#999]'
189195
: isThreejs
190196
? 'border-t border-blue-900/30 bg-gradient-to-b from-[#080c18] to-[#050509] py-6 text-xs text-slate-400'
191-
: theme === 'dark'
192-
? 'border-t border-slate-800 bg-gradient-to-b from-[#111120] to-[#050509] py-6 text-xs text-slate-400'
193-
: 'border-t border-slate-200 bg-gradient-to-b from-slate-50 to-slate-100 py-6 text-xs text-slate-600'
197+
: isMinecraft
198+
? 'border-t-2 border-[#3b3b3b] bg-[#8b8b8b] py-6 text-xs text-[#3b3b3b]'
199+
: theme === 'dark'
200+
? 'border-t border-slate-800 bg-gradient-to-b from-[#111120] to-[#050509] py-6 text-xs text-slate-400'
201+
: 'border-t border-slate-200 bg-gradient-to-b from-slate-50 to-slate-100 py-6 text-xs text-slate-600'
194202

195203
const navInitials = (() => {
196204
const name = (hero.title || '').trim()
@@ -348,7 +356,7 @@ function App() {
348356
<div className="mx-auto flex max-w-5xl items-center justify-between px-4 py-3 sm:px-6 sm:py-4">
349357
<Link
350358
to="/"
351-
className={`inline-flex items-center gap-2 no-underline ${isHacker ? 'text-green-500' : isNetflix ? 'text-white' : 'text-slate-900 dark:text-slate-100'}`}
359+
className={`inline-flex items-center gap-2 no-underline ${isHacker ? 'text-green-500' : isNetflix ? 'text-white' : isMinecraft ? 'text-white' : 'text-slate-900 dark:text-slate-100'}`}
352360
aria-label={`${hero.title} home`}
353361
>
354362
{isHacker ? (
@@ -358,6 +366,12 @@ function App() {
358366
</span>
359367
) : isNetflix ? (
360368
<span className="text-lg font-black text-[#e50914] tracking-tight">{navInitials}</span>
369+
) : isMinecraft ? (
370+
<span className="relative inline-block border-2 border-[#3b3b3b] bg-[#5c7a29] px-2 py-0.5 text-sm font-bold text-white drop-shadow-[1px_1px_0_#3f3f00]">
371+
<span className="pointer-events-none absolute inset-0 border-t border-l border-[#8acd32]" />
372+
<span className="pointer-events-none absolute inset-0 border-b border-r border-[#3a5a19]" />
373+
<span className="relative">{navInitials}</span>
374+
</span>
361375
) : (
362376
<span className="flex h-8 w-8 items-center justify-center rounded-xl bg-gradient-to-tr from-blue-600 to-cyan-400 text-xs font-semibold text-[#050509]" aria-hidden="true">
363377
{navInitials}
@@ -386,6 +400,18 @@ function App() {
386400
<Link to="/projects" className="transition hover:text-white">Projects</Link>
387401
<a href={`${import.meta.env.BASE_URL}#stats`} className="transition hover:text-white">Stats</a>
388402
</nav>
403+
) : isMinecraft ? (
404+
<nav className="hidden md:flex items-center gap-1 text-xs font-bold" aria-label="Primary">
405+
{(['/', '/videos', '/blogs', '/projects'] as const).map((path, i) => {
406+
const labels = ['Home', 'Videos', 'Blogs', 'Projects']
407+
return (
408+
<Link key={path} to={path} className="relative border-2 border-[#3b3b3b] bg-[#6b6b6b] px-2.5 py-1 text-[#d4d4d4] transition-all hover:bg-[#555555] hover:text-white">
409+
{labels[i]}
410+
</Link>
411+
)
412+
})}
413+
<a href={`${import.meta.env.BASE_URL}#stats`} className="relative border-2 border-[#3b3b3b] bg-[#6b6b6b] px-2.5 py-1 text-[#d4d4d4] transition-all hover:bg-[#555555] hover:text-white">Stats</a>
414+
</nav>
389415
) : (
390416
<nav className="hidden md:flex items-center gap-3 text-xs font-medium text-slate-700 dark:text-slate-300" aria-label="Primary">
391417
<Link to="/" className="border-b border-transparent pb-0.5 transition-colors hover:border-slate-500 hover:text-slate-900 dark:hover:border-slate-400 dark:hover:text-slate-50">Home</Link>
@@ -408,7 +434,9 @@ function App() {
408434
? 'border-green-900 bg-green-950/40 text-green-600 hover:bg-green-900/30'
409435
: isNetflix
410436
? 'border-white/10 bg-white/5 text-[#e5e5e5] hover:bg-white/10'
411-
: 'border-indigo-300/60 bg-indigo-50 text-indigo-600 hover:bg-indigo-100 dark:border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:bg-indigo-500/20'
437+
: isMinecraft
438+
? 'border-[#3b3b3b] bg-[#5c7a29] text-white hover:bg-[#6b8e3e] rounded-none'
439+
: 'border-indigo-300/60 bg-indigo-50 text-indigo-600 hover:bg-indigo-100 dark:border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:bg-indigo-500/20'
412440
}`}
413441
>
414442
<ForkIcon className="h-3 w-3 fill-current" />
@@ -421,7 +449,7 @@ function App() {
421449
<button
422450
type="button"
423451
onClick={() => setMobileMenuOpen((prev) => !prev)}
424-
className={`flex h-8 w-8 items-center justify-center rounded-md transition ${isHacker ? 'text-green-700 hover:bg-green-900/20' : isNetflix ? 'text-white hover:bg-white/10' : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800'}`}
452+
className={`flex h-8 w-8 items-center justify-center rounded-md transition ${isHacker ? 'text-green-700 hover:bg-green-900/20' : isNetflix ? 'text-white hover:bg-white/10' : isMinecraft ? 'text-[#3b3b3b] hover:bg-[#6b6b6b]' : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800'}`}
425453
aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'}
426454
>
427455
{mobileMenuOpen ? (
@@ -440,7 +468,7 @@ function App() {
440468
{/* Mobile dropdown menu */}
441469
{mobileMenuOpen && (
442470
<nav
443-
className={`border-t md:hidden backdrop-blur ${isHacker ? 'border-green-900/60 bg-black/95 font-mono text-xs text-green-700' : isNetflix ? 'border-white/5 bg-[#141414]/95 text-sm text-[#e5e5e5]' : 'border-slate-200 bg-slate-50/95 dark:border-white/5 dark:bg-[#050509]/95'}`}
471+
className={`border-t md:hidden backdrop-blur ${isHacker ? 'border-green-900/60 bg-black/95 font-mono text-xs text-green-700' : isNetflix ? 'border-white/5 bg-[#141414]/95 text-sm text-[#e5e5e5]' : isMinecraft ? 'border-[#3b3b3b] bg-[#8b8b8b]/95 text-sm text-[#3b3b3b] font-bold' : 'border-slate-200 bg-slate-50/95 dark:border-white/5 dark:bg-[#050509]/95'}`}
444472
aria-label="Mobile navigation"
445473
>
446474
<div className={`mx-auto flex max-w-5xl flex-col px-4 py-2 ${isHacker ? '' : isNetflix ? 'font-medium' : 'text-sm font-medium text-slate-700 dark:text-slate-300'}`}>

src/lib/activeTemplate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { githubConfig } from '../generated/githubData'
22

3-
export type TemplateName = 'minimal' | 'classic' | 'bento' | 'hacker' | 'netflix' | 'threejs'
3+
export type TemplateName = 'minimal' | 'classic' | 'bento' | 'hacker' | 'netflix' | 'threejs' | 'minecraft'
44

55
export const DEFAULT_TEMPLATE: TemplateName = 'threejs'
66

src/pages/BlogPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import BentoBlogPage from '../templates/bento/BlogPage'
55
import HackerBlogPage from '../templates/hacker/BlogPage'
66
import NetflixBlogPage from '../templates/netflix/BlogPage'
77
import ThreejsBlogPage from '../templates/threejs/BlogPage'
8+
import MinecraftBlogPage from '../templates/minecraft/BlogPage'
89

910
export default function BlogPage() {
1011
if (activeTemplate === 'classic') return <ClassicBlogPage />
1112
if (activeTemplate === 'bento') return <BentoBlogPage />
1213
if (activeTemplate === 'hacker') return <HackerBlogPage />
1314
if (activeTemplate === 'netflix') return <NetflixBlogPage />
1415
if (activeTemplate === 'threejs') return <ThreejsBlogPage />
16+
if (activeTemplate === 'minecraft') return <MinecraftBlogPage />
1517
return <MinimalBlogPage />
1618
}

src/pages/BlogsPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import BentoBlogsPage from '../templates/bento/BlogsPage'
55
import HackerBlogsPage from '../templates/hacker/BlogsPage'
66
import NetflixBlogsPage from '../templates/netflix/BlogsPage'
77
import ThreejsBlogsPage from '../templates/threejs/BlogsPage'
8+
import MinecraftBlogsPage from '../templates/minecraft/BlogsPage'
89

910
export default function BlogsPage() {
1011
if (activeTemplate === 'classic') return <ClassicBlogsPage />
1112
if (activeTemplate === 'bento') return <BentoBlogsPage />
1213
if (activeTemplate === 'hacker') return <HackerBlogsPage />
1314
if (activeTemplate === 'netflix') return <NetflixBlogsPage />
1415
if (activeTemplate === 'threejs') return <ThreejsBlogsPage />
16+
if (activeTemplate === 'minecraft') return <MinecraftBlogsPage />
1517
return <MinimalBlogsPage />
1618
}

src/pages/HomePage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import BentoHomePage from '../templates/bento/HomePage'
55
import HackerHomePage from '../templates/hacker/HomePage'
66
import NetflixHomePage from '../templates/netflix/HomePage'
77
import ThreejsHomePage from '../templates/threejs/HomePage'
8+
import MinecraftHomePage from '../templates/minecraft/HomePage'
89

910
export default function HomePage() {
1011
if (activeTemplate === 'classic') return <ClassicHomePage />
1112
if (activeTemplate === 'bento') return <BentoHomePage />
1213
if (activeTemplate === 'hacker') return <HackerHomePage />
1314
if (activeTemplate === 'netflix') return <NetflixHomePage />
1415
if (activeTemplate === 'threejs') return <ThreejsHomePage />
16+
if (activeTemplate === 'minecraft') return <MinecraftHomePage />
1517
return <MinimalHomePage />
1618
}

src/pages/ProjectsPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import BentoProjectsPage from '../templates/bento/ProjectsPage'
55
import HackerProjectsPage from '../templates/hacker/ProjectsPage'
66
import NetflixProjectsPage from '../templates/netflix/ProjectsPage'
77
import ThreejsProjectsPage from '../templates/threejs/ProjectsPage'
8+
import MinecraftProjectsPage from '../templates/minecraft/ProjectsPage'
89

910
export default function ProjectsPage() {
1011
if (activeTemplate === 'classic') return <ClassicProjectsPage />
1112
if (activeTemplate === 'bento') return <BentoProjectsPage />
1213
if (activeTemplate === 'hacker') return <HackerProjectsPage />
1314
if (activeTemplate === 'netflix') return <NetflixProjectsPage />
1415
if (activeTemplate === 'threejs') return <ThreejsProjectsPage />
16+
if (activeTemplate === 'minecraft') return <MinecraftProjectsPage />
1517
return <MinimalProjectsPage />
1618
}

src/pages/VideosPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import BentoVideosPage from '../templates/bento/VideosPage'
55
import HackerVideosPage from '../templates/hacker/VideosPage'
66
import NetflixVideosPage from '../templates/netflix/VideosPage'
77
import ThreejsVideosPage from '../templates/threejs/VideosPage'
8+
import MinecraftVideosPage from '../templates/minecraft/VideosPage'
89

910
export default function VideosPage() {
1011
if (activeTemplate === 'classic') return <ClassicVideosPage />
1112
if (activeTemplate === 'bento') return <BentoVideosPage />
1213
if (activeTemplate === 'hacker') return <HackerVideosPage />
1314
if (activeTemplate === 'netflix') return <NetflixVideosPage />
1415
if (activeTemplate === 'threejs') return <ThreejsVideosPage />
16+
if (activeTemplate === 'minecraft') return <MinecraftVideosPage />
1517
return <MinimalVideosPage />
1618
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Link } from 'react-router-dom'
2+
import type { Blog } from '../../types/contentTypes'
3+
4+
function stripMarkdown(text: string, maxLen: number): string {
5+
const plain = text
6+
.replace(/#{1,6}\s/g, '')
7+
.replace(/\*\*([^*]+)\*\*/g, '$1')
8+
.replace(/\*([^*]+)\*/g, '$1')
9+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
10+
.replace(/`[^`]+`/g, '')
11+
.replace(/\n+/g, ' ')
12+
.trim()
13+
return plain.length <= maxLen ? plain : plain.slice(0, maxLen).trim() + '…'
14+
}
15+
16+
export default function BlogCard({ blog, featured }: { blog: Blog; featured?: boolean }) {
17+
const date = blog.createdAt?.slice(0, 10) ?? '????-??-??'
18+
const excerpt = stripMarkdown(blog.content, 100)
19+
20+
return (
21+
<Link
22+
to={`/blog/${blog.id}`}
23+
className="group relative block border-2 border-[#3b3b3b] bg-[#8b8b8b] p-3 transition-all hover:border-[#ffffff80] hover:-translate-y-0.5 active:translate-y-0"
24+
style={{ imageRendering: 'pixelated' }}
25+
>
26+
{/* Highlight edge (top & left) */}
27+
<div className="pointer-events-none absolute inset-0 border-t-2 border-l-2 border-[#c6c6c6]" />
28+
{/* Shadow edge (bottom & right) */}
29+
<div className="pointer-events-none absolute inset-0 border-b-2 border-r-2 border-[#555555]" />
30+
31+
<div className="relative flex items-start gap-2">
32+
<span className="mt-0.5 shrink-0 text-[#5c7a29] text-lg leading-none"></span>
33+
<div className="min-w-0">
34+
<p className="font-bold text-white drop-shadow-[2px_2px_0_#3f3f00]">
35+
{featured && (
36+
<span className="mr-1.5 bg-[#5c7a29] px-1 py-0.5 text-[10px] text-white uppercase tracking-wider">★ Featured</span>
37+
)}
38+
{blog.title || 'Untitled'}
39+
</p>
40+
{excerpt && (
41+
<p className="mt-1 text-xs text-[#d4d4d4] line-clamp-2">{excerpt}</p>
42+
)}
43+
<p className="mt-1 text-[11px] text-[#a0a0a0]">{date}</p>
44+
</div>
45+
</div>
46+
</Link>
47+
)
48+
}

0 commit comments

Comments
 (0)