Skip to content

feat(blog): theme cover-fallback gradients by library#941

Merged
tannerlinsley merged 1 commit into
mainfrom
taren/mystifying-lalande-de38b3
May 22, 2026
Merged

feat(blog): theme cover-fallback gradients by library#941
tannerlinsley merged 1 commit into
mainfrom
taren/mystifying-lalande-de38b3

Conversation

@tannerlinsley
Copy link
Copy Markdown
Member

@tannerlinsley tannerlinsley commented May 22, 2026

Summary

Blog posts without an explicit headerImage now generate their cover-fallback gradient palette around the library's primary hue from libraries.ts (Virtual → purple, Query → red, AI → pink, etc.) so the placeholder feels branded instead of random. Posts without a library frontmatter keep the existing slug-hashed palette.

Also reworked the blob generator so the result reads as a soft watercolor wash rather than a single centered radial or polka-dot accents:

  • Library hues drive a 5-stop palette with mixed lightness (dark/light alternation) so adjacent blobs read as distinct color regions even when heavily blurred.
  • 10 anchors (4 wash + 6 accent) scattered across the canvas, each jittered per-slug — no two posts share a layout.
  • Ellipses use ry ≈ 1.5–2× rx to compensate for wide 5:2 / 16:9 containers so blobs don't squish into horizontal bands.
  • Soft falloff stops (85–120%) so blobs feather into each other.
  • A linear base tint sits underneath so edges never wash out to the wrapper background.

Test plan

  • Virtual blog post header renders purple/violet gradient (library hue 270)
  • npm postmortem (no library) renders distinct rose/dusk slug-hashed gradient
  • BlogCard list shows themed thumbnails consistent with detail-page headers
  • Same slug always renders the same gradient (deterministic FNV hash + seeded xorshift RNG)

Summary by CodeRabbit

  • New Features
    • Blog post covers now feature library-aware gradient backgrounds that create more diverse and visually distinct styling based on each post's associated library or category.

Review Change Stack

Blog posts without a header image now generate a gradient palette around
their library's primary hue (Virtual → purple, Query → red, etc.) instead
of picking one of eight slug-hashed palettes. Posts without a `library`
frontmatter keep the existing slug-hash behavior.

Also reworked the blob generator so gradients read as soft watercolor
washes: bigger radii with ry biased > rx to compensate for wide
containers, anchors scattered across the canvas with per-slug jitter, and
soft falloff stops so blobs blend into each other rather than rendering as
discrete circles. A linear base tint sits underneath so edges never wash
out to the wrapper background.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

📝 Walkthrough

Walkthrough

This PR extends blog cover fallback styling with library-aware gradients. The gradient system is refactored to support color palettes derived from library metadata, while the blog post data layer and component tree are extended to flow the library information through to the styling layer.

Changes

Library-aware blog cover gradients

Layer / File(s) Summary
Library-aware gradient system
src/utils/ogGradient.ts
Gradient generation refactored from slug-only random palettes to a library-aware system supporting brand-specific colors. Blob model changes from simple circles to ellipses with per-blob color stops. Introduces LIBRARY_HUES mapping and paletteFromHue for library-based color derivation, with slug-hash fallback. CSS output layers radial blob gradients with a linear tint base.
Blog post library metadata propagation
src/utils/blog.functions.ts, src/routes/blog.$.tsx
fetchBlogPost server function returns post library metadata, route loader unpacks the library value, and BlogPost component receives it for downstream consumption.
Component integration with library styling
src/components/CoverFallback.tsx, src/components/BlogCard.tsx
CoverFallback accepts optional library prop and passes it to gradient generation. BlogCard updates to render CoverFallback with multi-line props including the library value.

Sequence Diagram(s)

sequenceDiagram
  participant BlogPost as BlogPost<br/>Component
  participant CoverFallback as CoverFallback<br/>Component
  participant gradientBg as gradientBackgroundCss<br/>Function
  BlogPost->>CoverFallback: render with slug, library
  CoverFallback->>gradientBg: call(slug, library)
  Note over gradientBg: paletteFor checks LIBRARY_HUES<br/>for library primary hue
  Note over gradientBg: blobsFor generates ellipse<br/>blobs with deterministic jitter
  Note over gradientBg: blobsToCss creates radial<br/>gradients + tint layer
  gradientBg-->>CoverFallback: CSS string
  CoverFallback->>CoverFallback: apply as backgroundImage style
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • TanStack/tanstack.com#911: Also extends blog card rendering to leverage post library metadata for visual theming and branding.

Suggested reviewers

  • LadyBluenotes
  • schiller-manuel

Poem

A library of hues now blooms,
Where posts once wore their slugs alone,
Elliptical gradients paint the rooms,
Brand colors in the blend are shown. 🎨✨
The cards now glow with purpose bright,
A colorful journey, pure delight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(blog): theme cover-fallback gradients by library' directly and precisely describes the main change: theming gradient backgrounds for cover fallbacks by library. It is specific, clear, and captures the primary purpose of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch taren/mystifying-lalande-de38b3

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
src/utils/ogGradient.ts (1)

139-142: ⚡ Quick win

Normalize library IDs before hue lookup.

This lookup is case-sensitive and also fails when the first CSV token is empty, causing silent fallback to slug palettes. Normalizing + selecting first non-empty token makes theming more robust.

Proposed patch
-  if (library) {
-    const firstId = library.split(',')[0]?.trim()
+  if (library) {
+    const firstId = library
+      .split(',')
+      .map((id) => id.trim().toLowerCase())
+      .find(Boolean)
     const baseHue = firstId ? LIBRARY_HUES[firstId] : undefined
     if (baseHue !== undefined) {
       return paletteFromHue(baseHue)
     }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/ogGradient.ts` around lines 139 - 142, When resolving the palette
hue from the CSV "library" string, normalize and skip empty tokens: split
library by ',', map each token to token.trim().toLowerCase() (or otherwise match
the casing used in LIBRARY_HUES), filter out empty strings, then take the first
non-empty token as firstId before looking up LIBRARY_HUES[firstId]; update the
existing logic around the variables library, firstId and baseHue so the lookup
is case-insensitive and does not pick an empty token (preserving the existing
fallback to slug palettes when no valid firstId is found).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/utils/ogGradient.ts`:
- Around line 139-142: When resolving the palette hue from the CSV "library"
string, normalize and skip empty tokens: split library by ',', map each token to
token.trim().toLowerCase() (or otherwise match the casing used in LIBRARY_HUES),
filter out empty strings, then take the first non-empty token as firstId before
looking up LIBRARY_HUES[firstId]; update the existing logic around the variables
library, firstId and baseHue so the lookup is case-insensitive and does not pick
an empty token (preserving the existing fallback to slug palettes when no valid
firstId is found).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8da1a73a-56e4-4521-86db-24eb9461e9a1

📥 Commits

Reviewing files that changed from the base of the PR and between 8faf458 and 61e808c.

📒 Files selected for processing (5)
  • src/components/BlogCard.tsx
  • src/components/CoverFallback.tsx
  • src/routes/blog.$.tsx
  • src/utils/blog.functions.ts
  • src/utils/ogGradient.ts

@tannerlinsley tannerlinsley merged commit 8735dde into main May 22, 2026
9 checks passed
@tannerlinsley tannerlinsley deleted the taren/mystifying-lalande-de38b3 branch May 22, 2026 22:16
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