fix(web): correct blog colors for the light workspace + thumbnails

Only the .rail sidebar is dark; the .workspace content area is on the
light (#f6f7f4) page background. The previous blog styling assumed a dark
workspace, so text was light-grey on white (unreadable) and the link
green was too light.

- Darken the global link color to #0a7d59 (readable on white); content
  links only — rail nav stays inherited.
- Repaint .blog-content (post HTML) for a light surface: dark body text,
  light code/pre, light borders.
- Blog index/post: dark titles, readable grey meta, light row borders;
  light-themed footer.
- Add post thumbnails to the /blog index from featured_image.url.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Anthony Ettinger 2026-06-08 12:21:46 +00:00
parent 7e11130467
commit ce79f02211
4 changed files with 81 additions and 40 deletions

View file

@ -69,12 +69,14 @@ export default async function BlogPostPage({
<SiteShell active="Blog"> <SiteShell active="Blog">
<article className="band" style={{ maxWidth: "48rem" }}> <article className="band" style={{ maxWidth: "48rem" }}>
<p style={{ marginBottom: "1.5rem" }}> <p style={{ marginBottom: "1.5rem" }}>
<Link href="/blog" style={{ color: "#b5beb2", textDecoration: "none" }}> <Link href="/blog" style={{ color: "#5b6b7a", textDecoration: "none" }}>
Blog Blog
</Link> </Link>
</p> </p>
<h1 style={{ fontSize: "2.2rem", margin: "0 0 0.5rem" }}>{post.title}</h1> <h1 style={{ fontSize: "2.2rem", margin: "0 0 0.5rem", color: "#101418" }}>
<div style={{ color: "#8a95a0", fontSize: "0.85rem", marginBottom: "2rem" }}> {post.title}
</h1>
<div style={{ color: "#5b6b7a", fontSize: "0.85rem", marginBottom: "2rem" }}>
{formatDate(post.published_at)} {formatDate(post.published_at)}
</div> </div>
{post.featured_image?.url ? ( {post.featured_image?.url ? (

View file

@ -17,6 +17,7 @@ type PostRow = {
slug: string; slug: string;
title: string; title: string;
excerpt: string | null; excerpt: string | null;
featured_image: { url?: string } | null;
published_at: string; published_at: string;
}; };
@ -32,7 +33,7 @@ export default async function BlogIndex(): Promise<ReactNode> {
const supabase = publicClient(); const supabase = publicClient();
const { data } = await supabase const { data } = await supabase
.from("blog_posts") .from("blog_posts")
.select("slug, title, excerpt, published_at") .select("slug, title, excerpt, featured_image, published_at")
.eq("status", "published") .eq("status", "published")
.order("published_at", { ascending: false }); .order("published_at", { ascending: false });
const posts = (data ?? []) as PostRow[]; const posts = (data ?? []) as PostRow[];
@ -50,35 +51,71 @@ export default async function BlogIndex(): Promise<ReactNode> {
</div> </div>
{posts.length === 0 ? ( {posts.length === 0 ? (
<p style={{ color: "#b5beb2" }}>No posts yet.</p> <p style={{ color: "#5b6b7a" }}>No posts yet.</p>
) : ( ) : (
<ul style={{ listStyle: "none", margin: 0, padding: 0 }}> <ul style={{ listStyle: "none", margin: 0, padding: 0 }}>
{posts.map((post) => ( {posts.map((post) => {
const thumb = post.featured_image?.url;
return (
<li <li
key={post.slug} key={post.slug}
style={{ style={{
padding: "1.25rem 0", padding: "1.25rem 0",
borderTop: "1px solid rgba(245, 247, 244, 0.12)", borderTop: "1px solid #e3e6e0",
}} }}
> >
<Link <Link
href={`/blog/${post.slug}`} href={`/blog/${post.slug}`}
style={{ color: "inherit", textDecoration: "none" }} style={{
display: "flex",
gap: "1rem",
color: "inherit",
textDecoration: "none",
alignItems: "flex-start",
}}
>
{thumb ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={thumb}
alt=""
style={{
width: "160px",
height: "100px",
objectFit: "cover",
borderRadius: "0.5rem",
flexShrink: 0,
border: "1px solid #e3e6e0",
}}
/>
) : null}
<div style={{ minWidth: 0 }}>
<h3
style={{
margin: "0 0 0.35rem",
fontSize: "1.25rem",
color: "#101418",
}}
> >
<h3 style={{ margin: "0 0 0.35rem", fontSize: "1.25rem" }}>
{post.title} {post.title}
</h3> </h3>
</Link>
<div <div
style={{ color: "#8a95a0", fontSize: "0.85rem", marginBottom: "0.5rem" }} style={{
color: "#5b6b7a",
fontSize: "0.85rem",
marginBottom: "0.5rem",
}}
> >
{formatDate(post.published_at)} {formatDate(post.published_at)}
</div> </div>
{post.excerpt ? ( {post.excerpt ? (
<p style={{ color: "#b5beb2", margin: 0 }}>{post.excerpt}</p> <p style={{ color: "#41505d", margin: 0 }}>{post.excerpt}</p>
) : null} ) : null}
</div>
</Link>
</li> </li>
))} );
})}
</ul> </ul>
)} )}
</div> </div>

View file

@ -56,13 +56,13 @@ export function SiteShell({
maxWidth: "72rem", maxWidth: "72rem",
margin: "2rem auto 0", margin: "2rem auto 0",
padding: "1.25rem 0", padding: "1.25rem 0",
borderTop: "1px solid rgba(245, 247, 244, 0.12)", borderTop: "1px solid #d9ded4",
display: "flex", display: "flex",
flexWrap: "wrap", flexWrap: "wrap",
gap: "0.75rem", gap: "0.75rem",
justifyContent: "space-between", justifyContent: "space-between",
fontSize: "0.85rem", fontSize: "0.85rem",
color: "#b5beb2", color: "#5b6b7a",
}} }}
> >
<span>© {new Date().getFullYear()} Profullstack, Inc. · LogicSRC</span> <span>© {new Date().getFullYear()} Profullstack, Inc. · LogicSRC</span>

View file

@ -15,7 +15,7 @@ body {
} }
a { a {
color: #5ac8a6; color: #0a7d59;
text-decoration: none; text-decoration: none;
} }
@ -703,18 +703,19 @@ pre {
} }
} }
/* Rendered blog post HTML (autoblog payload) on the dark workspace. */ /* Rendered blog post HTML (autoblog payload) on the light workspace. */
.blog-content { .blog-content {
color: #e7ebe4; color: #1f2933;
} }
.blog-content a { .blog-content a {
color: #5ac8a6; color: #0a7d59;
} }
.blog-content h2, .blog-content h2,
.blog-content h3, .blog-content h3,
.blog-content h4 { .blog-content h4 {
margin: 1.75rem 0 0.5rem; margin: 1.75rem 0 0.5rem;
line-height: 1.25; line-height: 1.25;
color: #101418;
} }
.blog-content p, .blog-content p,
.blog-content ul, .blog-content ul,
@ -728,13 +729,14 @@ pre {
border-radius: 0.5rem; border-radius: 0.5rem;
} }
.blog-content pre { .blog-content pre {
background: rgba(0, 0, 0, 0.32); background: #f1f3ef;
border: 1px solid #e3e6e0;
padding: 1rem; padding: 1rem;
border-radius: 0.5rem; border-radius: 0.5rem;
overflow: auto; overflow: auto;
} }
.blog-content code { .blog-content code {
background: rgba(0, 0, 0, 0.32); background: rgba(16, 20, 24, 0.06);
padding: 0.1rem 0.35rem; padding: 0.1rem 0.35rem;
border-radius: 0.25rem; border-radius: 0.25rem;
font-size: 0.9em; font-size: 0.9em;
@ -744,7 +746,7 @@ pre {
padding: 0; padding: 0;
} }
.blog-content blockquote { .blog-content blockquote {
border-left: 3px solid rgba(245, 247, 244, 0.2); border-left: 3px solid #d9ded4;
padding-left: 1rem; padding-left: 1rem;
color: #b5beb2; color: #5b6b7a;
} }