Skip to content

chore(blog): improved ssr#7640

Open
mhartington wants to merge 2 commits intomainfrom
ssr-improvmenets
Open

chore(blog): improved ssr#7640
mhartington wants to merge 2 commits intomainfrom
ssr-improvmenets

Conversation

@mhartington
Copy link
Member

@mhartington mhartington commented Mar 12, 2026

Summary by CodeRabbit

  • New Features

    • Real post previews in the blog loading state with interactive tag filters and visible dates/excerpts
  • UI/UX

    • Adjusted author spacing on post cards and updated header/navigation hover styling
    • Tweaked newsletter background gradient and newsletter status messaging appearance
  • Behavior

    • Improved sync between URL query parameters and blog listing state
    • Persisted theme storage key and initialization refined for more consistent theming and accessibility (aria-live for status)

@vercel
Copy link

vercel bot commented Mar 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blog Ready Ready Preview, Comment Mar 12, 2026 8:38pm
docs Ready Ready Preview, Comment Mar 12, 2026 8:38pm
eclipse Ready Ready Preview, Comment Mar 12, 2026 8:38pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

Walkthrough

Multiple UI and behavior updates across the blog app and shared UI package: the blog page Suspense fallback now renders real tag links and post previews; layout navigation URLs and ThemeProvider storage key were adjusted; newsletter, theme-provider, navigation, BlogGrid, PostCard, and a CSS gradient were modified.

Changes

Cohort / File(s) Summary
Blog Page Fallback UI
apps/blog/src/app/(blog)/page.tsx
Replaces skeleton placeholders with interactive fallback content: adds staticFallbackItems (first 12 posts) and staticFallbackTags (first 10 unique tags); renders "Show all" and tag links, post preview articles with title, optional formatted date (formatDate), and excerpt; adds Link, formatDate, formatTag, and withBlogBasePath imports.
Layout & Nav URLs
apps/blog/src/app/(blog)/layout.tsx
Replaced several navigation href values with absolute https://www.prisma.io URLs and changed ThemeProvider's storageKey from "blog-theme" to "theme".
Global CSS
apps/blog/src/app/global.css
Adjusted .newsletter-bg linear-gradient stops: increased transparency mix at first stop and replaced second color-mix with a direct var(--color-background-default) reference; formatting changes.
BlogGrid / PostCard
apps/blog/src/components/BlogGrid.tsx, apps/blog/src/components/PostCard.tsx
BlogGrid: added searchParams to effect dependency array to resync state with URL changes. PostCard: adjusted author metadata container classes (mt-4 md:mt-0 and changed featured/non-featured flex/margin behavior), altering author row spacing/alignment.
Newsletter UI
packages/ui/src/components/newsletter.tsx
Consolidates status rendering into a single statusMessage computed value; replaces three separate conditionals with one status container (role="status"/aria-live="polite"); header text color class changed from white to neutral.
Theme Provider Logic
packages/ui/src/components/theme-provider.tsx
Reworks initialization: introduces toTheme/getInitialTheme, derives initial theme/resolvedTheme synchronously from storage/system at state init, removes prior eager mount effect and applies resolved theme via effect when it changes; persists changes to localStorage as before.
Web Navigation Styling
packages/ui/src/components/web-navigation.tsx
Adds additional styling modifiers to the first header NavigationMenuLink (hover/focus/active/bg transparency and cursor behavior); href remains https://www.prisma.io/.
Manifest
package.json (appears in two manifests)
Small manifest references noted in summaries; no API signature changes detected.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'improved ssr' is too vague and generic; it doesn't clearly describe what specific SSR improvements were made across the multiple files changed. Consider a more specific title that captures the main changes, such as 'refactor(blog): improve ssr with static fallback content and theme initialization' to better convey the scope of improvements.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@argos-ci
Copy link

argos-ci bot commented Mar 12, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ⚠️ Changes detected (Review) 1 removed Mar 12, 2026, 8:42 PM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/blog/src/app/(blog)/page.tsx (1)

98-141: Consider sharing the fallback preview markup with the loaded grid.

page.tsx now owns a second tag-pill/post-preview rendering path beside BlogGrid. That will be easy to drift the next time the blog card markup or styling changes. Extracting the preview pieces into shared helpers/components would keep the streamed and settled states aligned.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blog/src/app/`(blog)/page.tsx around lines 98 - 141, The fallback markup
duplicates BlogGrid's tag-pill and post-preview rendering (see
staticFallbackTags, staticFallbackItems, formatTag, formatDate and BlogGrid) —
extract shared components/helpers (e.g., TagPill and PostPreview) that
encapsulate the pill link and article card markup + props (tag, post) and
replace the inline fallback mapping with those components, then update BlogGrid
to use the same TagPill and PostPreview so both streamed (fallback) and settled
render paths share identical markup and styling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/blog/src/app/`(blog)/page.tsx:
- Around line 85-86: The fallback currently uses staticFallbackItems and
staticFallbackTags and ignores the active tag/searchParams, causing unrelated
posts to render; update BlogHome (and its fallback logic around
staticFallbackItems/staticFallbackTags) to accept the active tag (from
searchParams.tag) as a prop, filter items and uniqueTags by that tag before
slicing (so staticFallbackItems = items filtered by tag then slice(0,12),
staticFallbackTags = uniqueTags filtered/ordered relative to the active tag then
slice(0,10)), and pass the active tag into BlogGrid so the fallback can also
apply the selected pill styling for the active tag.

---

Nitpick comments:
In `@apps/blog/src/app/`(blog)/page.tsx:
- Around line 98-141: The fallback markup duplicates BlogGrid's tag-pill and
post-preview rendering (see staticFallbackTags, staticFallbackItems, formatTag,
formatDate and BlogGrid) — extract shared components/helpers (e.g., TagPill and
PostPreview) that encapsulate the pill link and article card markup + props
(tag, post) and replace the inline fallback mapping with those components, then
update BlogGrid to use the same TagPill and PostPreview so both streamed
(fallback) and settled render paths share identical markup and styling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c576ea9e-b105-45fd-ab95-700db0530435

📥 Commits

Reviewing files that changed from the base of the PR and between 65c0ea1 and dda8332.

📒 Files selected for processing (1)
  • apps/blog/src/app/(blog)/page.tsx

Comment on lines +85 to +86
const staticFallbackItems = items.slice(0, 12);
const staticFallbackTags = uniqueTags.slice(0, 10);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make the fallback respect the active tag filter.

This fallback always renders the first 12 unfiltered posts, and BlogHome never reads searchParams. On /blog?tag=..., users will briefly see unrelated content until BlogGrid resolves, which makes the new SSR state misleading.

Suggested direction
+ // derive `activeTag` from the page's search params before building fallback data
+ const fallbackSourceItems = activeTag
+   ? items.filter((item) => item.tags?.includes(activeTag))
+   : items;
- const staticFallbackItems = items.slice(0, 12);
+ const staticFallbackItems = fallbackSourceItems.slice(0, 12);

If you thread the active tag into this component, you can also reflect the selected pill styling in the fallback.

Also applies to: 97-142

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blog/src/app/`(blog)/page.tsx around lines 85 - 86, The fallback
currently uses staticFallbackItems and staticFallbackTags and ignores the active
tag/searchParams, causing unrelated posts to render; update BlogHome (and its
fallback logic around staticFallbackItems/staticFallbackTags) to accept the
active tag (from searchParams.tag) as a prop, filter items and uniqueTags by
that tag before slicing (so staticFallbackItems = items filtered by tag then
slice(0,12), staticFallbackTags = uniqueTags filtered/ordered relative to the
active tag then slice(0,10)), and pass the active tag into BlogGrid so the
fallback can also apply the selected pill styling for the active tag.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/ui/src/components/newsletter.tsx (1)

103-107: Use assertive live region for errors.

Error feedback should be announced immediately; role="status" with polite live mode can delay it.

Proposed accessibility tweak
-              <p
-                className={cn("text-sm self-start", statusMessage.className)}
-                role="status"
-                aria-live="polite"
-              >
+              <p
+                className={cn("text-sm self-start", statusMessage.className)}
+                role={error ? "alert" : "status"}
+                aria-live={error ? "assertive" : "polite"}
+              >
                 {statusMessage.text}
               </p>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/newsletter.tsx` around lines 103 - 107, The error
message paragraph in newsletter.tsx currently uses role="status" and
aria-live="polite", which can delay announcing errors; update the component that
renders the <p> (the element referencing statusMessage and
statusMessage.className) so that when the message represents an error it uses an
assertive live region (e.g., role="alert" or aria-live="assertive") instead of
polite; implement a conditional around statusMessage (or a
statusMessage.type/severity check) to switch the role/aria-live only for errors
while keeping polite for non-error notices.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/theme-provider.tsx`:
- Around line 66-72: getInitialTheme currently falls back to the hardcoded
string "system" which ignores the configured defaultTheme; change the fallback
to use the provided defaultTheme instead. Update the return in getInitialTheme
(and the undefined-window early return) so it returns defaultTheme when no
stored value exists (e.g., return stored ?? defaultTheme) and keep existing
system-handling elsewhere if defaultTheme === "system"; reference
getInitialTheme, defaultTheme, storageKey, and toTheme to locate the code to
change.

---

Nitpick comments:
In `@packages/ui/src/components/newsletter.tsx`:
- Around line 103-107: The error message paragraph in newsletter.tsx currently
uses role="status" and aria-live="polite", which can delay announcing errors;
update the component that renders the <p> (the element referencing statusMessage
and statusMessage.className) so that when the message represents an error it
uses an assertive live region (e.g., role="alert" or aria-live="assertive")
instead of polite; implement a conditional around statusMessage (or a
statusMessage.type/severity check) to switch the role/aria-live only for errors
while keeping polite for non-error notices.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 830ef817-ce6f-4211-80ad-10e038d9f5fb

📥 Commits

Reviewing files that changed from the base of the PR and between dda8332 and be68f99.

📒 Files selected for processing (7)
  • apps/blog/src/app/(blog)/layout.tsx
  • apps/blog/src/app/global.css
  • apps/blog/src/components/BlogGrid.tsx
  • apps/blog/src/components/PostCard.tsx
  • packages/ui/src/components/newsletter.tsx
  • packages/ui/src/components/theme-provider.tsx
  • packages/ui/src/components/web-navigation.tsx

Comment on lines +66 to +72
const getInitialTheme = (): Theme => {
if (typeof window === "undefined") return defaultTheme;

const stored = toTheme(localStorage.getItem(storageKey));
// Requested behavior: prefer stored value, otherwise system preference.
return stored ?? "system";
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

defaultTheme is ignored when storage is empty.

On Line 71, fallback is hardcoded to "system", so callers passing defaultTheme="light" or "dark" won’t get their configured default on first load.

Proposed fix
-    // Requested behavior: prefer stored value, otherwise system preference.
-    return stored ?? "system";
+    // Prefer stored value; otherwise respect configured default.
+    return stored ?? defaultTheme;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/theme-provider.tsx` around lines 66 - 72,
getInitialTheme currently falls back to the hardcoded string "system" which
ignores the configured defaultTheme; change the fallback to use the provided
defaultTheme instead. Update the return in getInitialTheme (and the
undefined-window early return) so it returns defaultTheme when no stored value
exists (e.g., return stored ?? defaultTheme) and keep existing system-handling
elsewhere if defaultTheme === "system"; reference getInitialTheme, defaultTheme,
storageKey, and toTheme to locate the code to change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant