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:
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 {