Self-hosted: add a Gallery layout for image-led blogs - #1470
Conversation
The roster had seven templates but three layouts: five of them render the identical tree and differ only in tokens. Nothing served a blog whose posts are pictures, which got a text list with a thumbnail beside it. Gallery makes the archive a grid of covers with the title and date as a quiet caption underneath, and no sidebar. It is the first layout-level theme that keeps the shared shell: the structure is three CSS rules the theme owns, so only the tile and the absent sidebar are components. A post with no usable image falls back to a typeset panel rather than leaving a hole in the grid.
Code Review by Qodo
1.
|
PR Summary by QodoSelf-hosted: add Gallery style template with image-grid archive layout
AI Description
Diagram
High-Level Assessment
Files changed (13)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 80d29516a9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| :root[data-style-template="gallery"] .blog-page-measure { | ||
| max-width: var(--theme-content-width); |
There was a problem hiding this comment.
Keep article pages at the reading measure
When Gallery is active, this selector widens every BlogPage, not only the archive: the post, search, and About routes all render through DefaultShell, and BlogPostBody explicitly uses max-w-none. Consequently, opening an article on a desktop expands its text from the existing max-w-3xl reading measure to 1200px, producing very long lines. Scope the widening to the archive/search grid rather than the shared page wrapper.
Useful? React with 👍 / 👎.
| :root[data-style-template="gallery"] .blog-sidebar-container { | ||
| display: none; |
There was a problem hiding this comment.
Avoid hiding direct editor sidebars with archive CSS
On Gallery instances, this global selector also matches the /publish and /edit/$author/$permlink layouts, which use blog-sidebar-container but directly render BlogSidebar rather than the theme's null Sidebar seam. The sidebar therefore disappears while its account/community hooks and useQuery calls still mount and issue the requests this theme is intended to avoid. Scope this rule to DefaultShell or make those routes resolve the theme sidebar seam.
Useful? React with 👍 / 👎.
|
Warning Review limit reached
Next review available in: 85 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (13)
📝 WalkthroughWalkthroughThe PR adds the Gallery style template, configuration label, theme stylesheet, image-grid layout, custom post card, sidebar override, registry entries, and test coverage. ChangesGallery template registration and configuration
Gallery styling and layout
Gallery components and registry
Validation
Estimated code review effort: 3 (Moderate) | ~20 minutes Mergeability Score: ⚪ Minimal · up to The Gallery layout and related registry, styling, and rendering changes are merge-ready after normal checks and review; no actionable merge-blocking risk remains. Sequence Diagram(s)sequenceDiagram
participant BlogPage
participant ArchiveList
participant GalleryPostCard
participant GallerySidebar
BlogPage->>ArchiveList: render archive
ArchiveList->>GalleryPostCard: provide post entry
GalleryPostCard->>GalleryPostCard: resolve image or fallback content
BlogPage->>GallerySidebar: resolve sidebar component
GallerySidebar-->>BlogPage: return null
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
|
Correction to the PR body: I cannot attach images through the API, so here are the measured results instead. Screenshots are on the build box and can be added to the PR by hand. Served the real built
Dark config confirmed separately: Worth recording for whoever reviews: the archive width needed the |
Code Review by Qodo
1.
|
|
All three fixed at 53bab1a, and the second one had a fourth instance the review did not name. Article and About pages lost their reading measure. Valid, and the worst of the three: I only ever looked at the archive. The sidebar rules reached /publish and /edit. Valid. Both routes render Verified by building the app and constructing both route shapes against the real stylesheet:
And per route, live: archive 1200px wide with 4 columns, article 768px, About 768px. Over-fetching. Valid, and embarrassing given #1463 fixed exactly this class of bug. The guard test was rewritten around these two scoping bugs rather than just the rules' existence, so a future edit that unscopes them fails rather than silently taking the editor's sidebar away again. |
|
Fixed at d8ddfbe, and measuring it changed the answer. You are right that the previous hint diverged, and the two-column case you name is the worst of it. But the suggested breakpoints of roughly 580, 860 and 1140 assume the container grows continuously with the viewport. It does not: Tailwind's
So the transitions are at 580, 768, 1024 and 1280, and three of the five ranges are a fixed pixel width rather than anything expressible as Verified by loading in a fresh context per viewport, since a cached larger candidate is kept and would otherwise make every reading after the first a copy of the first:
No under-fetch anywhere, and the common desktop case drops from 600 to 320. The two remaining 1.7x rows are not the hint's doing: |
|
Correct, fixed at 7003f23. And my previous comment's table was wrong: it said the 580 to 767 range grows to 354, when my own measurements in that same run showed 700px rendering a 290px tile. I misread my own data and published it as a table, which is worse than not having measured. The container caps at 640 there exactly as you say. Measured densely across the range this time:
The formula now stops at 639 and hands over to a flat 290 through 767, which dovetails exactly: at 639 the calculation yields 289.5. Verified at both pixel ratios with a fresh context per width:
Both selections you predicted are what changed. No under-fetch at either ratio anywhere in the range; at 2x every row is now between 1.03x and 1.27x of what is displayed. Two rows still sit near 1.7x at 1x (390 and 768 to 1023), and those are the candidate ladder rather than the hint: |
Closes #1467.
The roster had seven templates but three layouts. Five of them (medium, minimal, magazine, developer, modern-gradient) carry no component overrides at all: they render the identical tree and differ in tokens. Nothing served a blog whose posts are pictures, which got a text list with a thumbnail beside it.
The layout
The archive becomes a grid of covers, with the title and date as a quiet caption underneath and no sidebar. The palette is a gallery wall on purpose: near-neutral grounds and a desaturated slate accent, because a saturated accent beside a photograph fights it. In dark mode the wall drops to #121212 so the pictures carry the brightness.
Gallery is the first layout-level theme that keeps the shared shell. Its structure is three CSS rules the theme owns, so only two things are components: the tile, and a Sidebar that renders nothing. That last one is a seam override rather than a CSS hide on purpose, because the default sidebar fetches followers, following and chain data, and hiding it in CSS would still run all of that for a column nobody sees.
A post with no usable image is not dropped and not given an empty box. Some posts in an image-led blog are text, and a hole in a grid reads as a bug, so those tiles fall back to a typeset panel carrying the title and first lines.
One shared-component change
BlogPagegains ablog-page-measureclass beside its existingmax-w-3xl. Nothing changes for any other template; it is a styling hook so Gallery can widen that one wrapper from its own stylesheet. MakingBlogPageread--theme-content-widthinstead was the tempting fix and would have been wrong: every other template declares 640px to 760px there, so it would have narrowed every existing blog's archive as a side effect of adding a theme.Verified by rendering it
Built the SPA and served the real
distthrough the published image's nginx with a config for an image-heavy account:The three layout rules are pinned by a new guard test, because they are silent when broken: delete the grid rule and Gallery becomes a single column that looks like a design choice, and the guard also asserts every layout selector keeps its
:rootprefix, since components.css is imported after the themes and would otherwise win at equal specificity.939 SPA tests, 465 hosting API tests, typecheck and a production build all pass. Roster, editor label, token and card-treatment guards updated for the eighth template.
Screenshots (desktop light, desktop dark, phone) attached in the next comment.
Summary by CodeRabbit