prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

commit 1f6f2c42f8ec2d345b4d1613aff621fb3da13d0c
parent dc2291ad22902e6e30261a262b935a794ac32f4a
Author: Damian Myrda <damian@prime8.dev>
Date:   Wed, 21 Jan 2026 17:52:58 -0600

refactor: use sticky header/footer, refactor Article component

- Change header and footer to position: sticky instead of fixed
- Remove isStandalone prop from Article, move back buttons to parent components
- Hide tab numbers on mobile
- Reduce experience image sizes on desktop
- Various spacing and padding adjustments

Diffstat:
Mweb/src/components/Article.astro | 55+++----------------------------------------------------
Mweb/src/components/Footer.astro | 6++----
Mweb/src/components/Header.astro | 17+++++++++--------
Mweb/src/components/pages/Blog.astro | 41++++++++++++++++++++++++++++++++++++++++-
Mweb/src/components/pages/Experience.astro | 28+++++++++++++---------------
Mweb/src/layouts/Layout.astro | 7++-----
Mweb/src/lib/readme.ts | 4++--
Mweb/src/pages/blog/[slug].astro | 34+++++++++++++++++++++++++++++++---
Mweb/src/pages/experience/[id].astro | 18+++++++++++-------
Mweb/src/pages/projects/[id].astro | 5++---
10 files changed, 115 insertions(+), 100 deletions(-)

diff --git a/web/src/components/Article.astro b/web/src/components/Article.astro @@ -3,24 +3,12 @@ import type Blog from "../lib/types/blog"; interface Props { blog: Blog; - isStandalone?: boolean; } -const { blog, isStandalone = true } = Astro.props; +const { blog } = Astro.props; --- <article class="blog-article" data-id={blog.id} data-blog-id={blog.id}> - { - isStandalone ? ( - <a href="/#blog" class="blog-back-btn"> - ← See all posts - </a> - ) : ( - <button class="blog-back-btn blog-back-btn-internal"> - <span class="color-dim">← Back to posts</span> - </button> - ) - } <header class="blog-article-header"> <div class="blog-article-header-top"> <h1 class="blog-article-title">{blog.title}</h1> @@ -64,26 +52,6 @@ const { blog, isStandalone = true } = Astro.props; </article> <style> - .blog-back-btn { - display: inline-block; - background: transparent; - border: 1px solid var(--border); - color: var(--fg-dim); - padding: 8px 16px; - cursor: pointer; - font-family: inherit; - font-size: 12px; - margin-bottom: 24px; - transition: all 0.15s; - text-decoration: none; - } - - .blog-back-btn:hover { - background: var(--bg-alt); - color: var(--accent); - border-color: var(--accent); - } - .blog-article { animation: fadeIn 0.2s ease; } @@ -160,7 +128,7 @@ const { blog, isStandalone = true } = Astro.props; font-size: 14px; line-height: 1.7; color: var(--fg); - padding-bottom: 64px; + padding-bottom: 32px; } .blog-article-content :global(p) { @@ -326,12 +294,6 @@ const { blog, isStandalone = true } = Astro.props; } @media (max-width: 768px) { - .blog-back-btn { - padding: 6px 12px; - font-size: 11px; - margin-bottom: 16px; - } - .blog-article-header { margin-bottom: 16px; padding-bottom: 12px; @@ -351,7 +313,6 @@ const { blog, isStandalone = true } = Astro.props; .blog-article-content { font-size: 13px; - padding-bottom: 32px; } .blog-article-content :global(h2) { @@ -385,9 +346,6 @@ const { blog, isStandalone = true } = Astro.props; </style> <script> - import { closeBlogPost } from "../scripts/ui/blog"; - import { updateUrl } from "../scripts/ui/url"; - document.addEventListener("DOMContentLoaded", () => { const shareButtons = document.querySelectorAll(".blog-share-btn"); @@ -397,7 +355,7 @@ const { blog, isStandalone = true } = Astro.props; const button = e.currentTarget as HTMLButtonElement; const blogId = button.dataset.blogId; const title = button.dataset.blogTitle; - const url = `${window.location.origin}/#blog/${blogId}`; + const url = `${window.location.origin}/blog/${blogId}`; if (navigator.share) { try { @@ -425,12 +383,5 @@ const { blog, isStandalone = true } = Astro.props; }, 2000); }); } - - document.querySelectorAll(".blog-back-btn-internal").forEach((btn) => { - btn.addEventListener("click", () => { - closeBlogPost(); - updateUrl(); - }); - }); }); </script> diff --git a/web/src/components/Footer.astro b/web/src/components/Footer.astro @@ -27,11 +27,9 @@ const { wakaTimeData } = Astro.props; padding: 0 12px; height: 24px; font-size: 12px; - position: fixed; + position: sticky; bottom: 0; - left: 0; - right: 0; - z-index: 10; + z-index: 100; } #statusbar .time { diff --git a/web/src/components/Header.astro b/web/src/components/Header.astro @@ -1,9 +1,9 @@ <header id="tabs"> <nav class="tab-nav" aria-label="Main navigation"> - <div class="tab active" data-tab="about" aria-current="page">[1] About</div> - <div class="tab" data-tab="experience">[2] Experience</div> - <div class="tab" data-tab="projects">[3] Projects</div> - <div class="tab" data-tab="blog">[4] Blog</div> + <div class="tab active" data-tab="about" aria-current="page"><span class="tab-num">[1] </span>About</div> + <div class="tab" data-tab="experience"><span class="tab-num">[2] </span>Experience</div> + <div class="tab" data-tab="projects"><span class="tab-num">[3] </span>Projects</div> + <div class="tab" data-tab="blog"><span class="tab-num">[4] </span>Blog</div> </nav> <div class="tab-spacer"></div> <div class="status" id="header-status"> @@ -28,11 +28,8 @@ border-bottom: 1px solid var(--border); padding: 0 12px; height: 36px; - position: fixed; + position: sticky; top: 0; - left: 0; - right: 0; - width: 100%; z-index: 100; -webkit-user-select: none; -moz-user-select: none; @@ -139,5 +136,9 @@ .status { display: none; } + + .tab-num { + display: none; + } } </style> diff --git a/web/src/components/pages/Blog.astro b/web/src/components/pages/Blog.astro @@ -29,6 +29,7 @@ const blogs = rawBlogs as Blog[]; <div id="blog-post-view" class="blog-post-view hidden"> <div class="blog-post-container"> + <button class="blog-back-btn">← Back to posts</button> {blogs.map((blog, i) => ( <div class="blog-post hidden" @@ -36,7 +37,7 @@ const blogs = rawBlogs as Blog[]; data-id={blog.id} data-blog-id={blog.id} > - <BlogArticle blog={blog} isStandalone={false} /> + <BlogArticle blog={blog} /> </div> ))} </div> @@ -156,6 +157,26 @@ const blogs = rawBlogs as Blog[]; display: none; } + .blog-back-btn { + display: inline-block; + background: transparent; + border: 1px solid var(--border); + color: var(--fg-dim); + padding: 8px 16px; + cursor: pointer; + font-family: inherit; + font-size: 12px; + margin-bottom: 24px; + transition: all 0.15s; + text-decoration: none; + } + + .blog-back-btn:hover { + background: var(--bg-alt); + color: var(--accent); + border-color: var(--accent); + } + @media (max-width: 768px) { .blog-list-container { padding: 16px; @@ -179,6 +200,12 @@ const blogs = rawBlogs as Blog[]; .blog-post-view { padding: 16px; } + + .blog-back-btn { + padding: 6px 12px; + font-size: 11px; + margin-bottom: 16px; + } } @media (max-width: 480px) { @@ -203,3 +230,15 @@ const blogs = rawBlogs as Blog[]; } } </style> + +<script> + import { closeBlogPost } from "../../scripts/ui/blog"; + import { updateUrl } from "../../scripts/ui/url"; + + document.addEventListener("DOMContentLoaded", () => { + document.querySelector(".blog-back-btn")?.addEventListener("click", () => { + closeBlogPost(); + updateUrl(); + }); + }); +</script> diff --git a/web/src/components/pages/Experience.astro b/web/src/components/pages/Experience.astro @@ -371,7 +371,6 @@ while (current <= timelineEnd) { border-radius: 4px; padding: 24px; width: 100%; - flex: 1; display: flex; flex-direction: column; gap: 0; @@ -486,18 +485,16 @@ while (current <= timelineEnd) { .exp-card-images { display: flex; - gap: 16px; + gap: 12px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); - flex: 1; - min-height: 0; + max-height: 180px; overflow: hidden; } .exp-image-hero { - flex: 1; - min-width: 0; + max-width: 280px; height: 100%; border-radius: 4px; border: 1px solid var(--border); @@ -514,17 +511,15 @@ while (current <= timelineEnd) { } .exp-image-grid { - width: 180px; - flex-shrink: 0; display: flex; - flex-direction: column; - gap: 4px; + flex-wrap: wrap; + gap: 8px; + max-width: 400px; } .exp-image-thumb { - width: 100%; - flex: 1; - min-height: 50px; + width: calc(50% - 4px); + height: 86px; border-radius: 4px; border: 1px solid var(--border); object-fit: cover; @@ -619,15 +614,19 @@ while (current <= timelineEnd) { .exp-card-images { flex-direction: column; gap: 12px; + max-height: none; + overflow: visible; } .exp-image-hero { + max-width: 100%; height: auto; max-height: 200px; } .exp-image-grid { width: 100%; + max-width: none; flex-direction: row; flex-wrap: wrap; gap: 8px; @@ -635,8 +634,7 @@ while (current <= timelineEnd) { .exp-image-thumb { width: calc(33.33% - 6px); - min-height: 60px; - max-height: 80px; + height: 80px; } } diff --git a/web/src/layouts/Layout.astro b/web/src/layouts/Layout.astro @@ -149,8 +149,6 @@ const ogImage = image line-height: 1.5; min-height: 100vh; overflow: hidden; - padding-top: 36px; - padding-bottom: 24px; } ::-webkit-scrollbar { @@ -218,7 +216,7 @@ const ogImage = image #app { display: flex; flex-direction: column; - height: calc(100vh - 36px - 24px); + height: 100vh; opacity: 0; } @@ -345,7 +343,6 @@ const ogImage = image body { font-size: 13px; overflow-y: auto; - padding-top: 52px; } #app { @@ -362,7 +359,7 @@ const ogImage = image .page { height: auto; - min-height: calc(100vh - 52px - 24px); + min-height: 100vh; } .page.active { diff --git a/web/src/lib/readme.ts b/web/src/lib/readme.ts @@ -2,10 +2,10 @@ import { marked } from "marked"; import type { Tokens, RendererThis } from "marked"; export function parseMarkdown(markdown: string): string { - return marked.parse(markdown) as string; + return marked.parse(removeFirstH1(markdown)) as string; } -export function removeFirstH1(markdown: string): string { +function removeFirstH1(markdown: string): string { return markdown.replace(/^#\s+.+\n+/, ""); } diff --git a/web/src/pages/blog/[slug].astro b/web/src/pages/blog/[slug].astro @@ -27,6 +27,7 @@ const keywords = blog.tags?.join(", "); > <main id="content"> <div class="blog-page"> + <a href="/#blog" class="blog-back-btn">← See more posts</a> <BlogArticle blog={blog} /> </div> <div id="blog-data" data-blog-id={blog.id}></div> @@ -62,9 +63,36 @@ const keywords = blog.tags?.join(", "); .blog-page { max-width: 720px; margin: 0 auto; - padding: 40px 24px; - padding-top: 24px; - min-height: calc(100vh - 36px); + padding: 40px 24px 0; + min-height: 100vh; + } + + .blog-back-btn { + display: inline-block; + background: transparent; + border: 1px solid var(--border); + color: var(--fg-dim); + padding: 8px 16px; + cursor: pointer; + font-family: inherit; + font-size: 12px; + margin-bottom: 24px; + transition: all 0.15s; + text-decoration: none; + } + + .blog-back-btn:hover { + background: var(--bg-alt); + color: var(--accent); + border-color: var(--accent); + } + + @media (max-width: 768px) { + .blog-back-btn { + padding: 6px 12px; + font-size: 11px; + margin-bottom: 16px; + } } </style> diff --git a/web/src/pages/experience/[id].astro b/web/src/pages/experience/[id].astro @@ -45,7 +45,7 @@ const description = `${experience.role} at ${experience.company}. ${experience.d > <main id="content"> <div class="experience-page"> - <a href="/#experience" class="back-btn">← Back to experience</a> + <a href="/#experience" class="back-btn">← See more experiences</a> <header class="experience-header"> <div class="header-top"> @@ -146,8 +146,7 @@ const description = `${experience.role} at ${experience.company}. ${experience.d max-width: 800px; margin: 0 auto; padding: 40px 24px; - padding-top: 24px; - min-height: calc(100vh - 36px); + min-height: 100vh; } .back-btn { @@ -266,13 +265,14 @@ const description = `${experience.role} at ${experience.company}. ${experience.d .image-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - gap: 16px; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 12px; } .experience-image { width: 100%; - height: auto; + max-height: 160px; + object-fit: cover; border-radius: 4px; border: 1px solid var(--border); cursor: pointer; @@ -304,7 +304,11 @@ const description = `${experience.role} at ${experience.company}. ${experience.d } .image-grid { - grid-template-columns: 1fr; + grid-template-columns: repeat(2, 1fr); + } + + .experience-image { + max-height: 120px; } } </style> diff --git a/web/src/pages/projects/[id].astro b/web/src/pages/projects/[id].astro @@ -35,7 +35,7 @@ const metadata = await fetchGitMetadataForProject(project); > <main id="content"> <div class="project-page"> - <a href="/#projects" class="back-btn">← Back to projects</a> + <a href="/#projects" class="back-btn">← See more projects</a> <header class="project-header"> <div class="header-top"> @@ -104,8 +104,7 @@ const metadata = await fetchGitMetadataForProject(project); max-width: 800px; margin: 0 auto; padding: 40px 24px; - padding-top: 24px; - min-height: calc(100vh - 36px); + min-height: 100vh; } .back-btn {