Replace grid-based shell with fixed-position layout using CSS custom properties
Intent: Move from a two-column CSS Grid wrapper (`.branch-review-shell`) that inline-reflowed page content, to a simpler model where the `<main>` element itself (`branch-page`) shrinks its max-width when the drawer is open, and the sidebar is positioned fixed on the right edge of the viewport.
Affected files: crates/pika-news/templates/detail.html
Evidence
@@ -168,19 +168,14 @@
- .branch-review-shell {
- display: grid;
- grid-template-columns: minmax(0, 1fr) 0;
- gap: 1.25rem;
- margin-top: 1rem;
- align-items: start;
- transition: grid-template-columns 180ms ease;
- }
- .branch-review-shell.discussion-open {
- grid-template-columns: minmax(0, 1fr) clamp(320px, 24vw, 440px);
+ .branch-page {
+ --discussion-sidebar-width: clamp(480px, 30vw, 620px);
+ --discussion-sidebar-gap: 1.5rem;
+ transition: max-width 180ms ease, padding-right 180ms ease;
}
- .branch-review-content {
- min-width: 0;
+ .branch-page.discussion-open {
+ max-width: calc(2200px - var(--discussion-sidebar-width) - var(--discussion-sidebar-gap));
+ padding-right: 0.5rem;
}
The old layout used a .branch-review-shell div with display: grid and two columns. When the discussion was closed the second column was 0; when open it expanded to clamp(320px, 24vw, 440px). This caused the entire page content to reflow inside the grid.
The new approach defines two CSS custom properties on .branch-page:
--discussion-sidebar-width: clamp(480px, 30vw, 620px) — wider than before (was 320–440px, now 480–620px)
--discussion-sidebar-gap: 1.5rem
When .discussion-open is toggled on .branch-page, the main content constrains itself with max-width: calc(2200px - var(--discussion-sidebar-width) - var(--discussion-sidebar-gap)) and adds a small padding-right. Both properties transition smoothly at 180ms. The .branch-review-content wrapper class is removed entirely since it is no longer needed.
Convert discussion sidebar to a fixed-position overlay panel
Intent: Position the discussion sidebar as a viewport-fixed panel on the right edge rather than an inline grid cell, giving it a drop shadow and full-height stretch. Use CSS sibling selectors instead of descendant selectors for open-state styling.
Affected files: crates/pika-news/templates/detail.html
Evidence
@@ -227,8 +222,8 @@
- min-height: min(76vh, 880px);
- height: min(76vh, 880px);
+ min-height: 100%;
+ height: 100%;
@@ -238,21 +233,30 @@
.discussion-sidebar-shell {
+ position: fixed;
+ top: 1.25rem;
+ right: 1.25rem;
+ bottom: 1.25rem;
+ width: var(--discussion-sidebar-width);
+ max-width: calc(100vw - 2rem);
min-width: 0;
opacity: 0;
pointer-events: none;
- transform: translateX(1rem);
- overflow: hidden;
+ transform: translateX(calc(100% + var(--discussion-sidebar-gap)));
+ z-index: 40;
transition: opacity 160ms ease, transform 180ms ease;
}
- .branch-review-shell.discussion-open .discussion-sidebar-shell {
+ .branch-page.discussion-open + .discussion-backdrop + .discussion-sidebar-shell {
opacity: 1;
pointer-events: auto;
transform: translateX(0);
}
@@ -255,8 +255,12 @@
.discussion-sidebar-shell .discussion-panel {
- position: sticky;
- top: 4.4rem;
+ position: relative;
+ top: 0;
+ min-height: 0;
+ height: 100%;
+ box-shadow: 0 22px 46px rgba(4, 9, 18, 0.22);
The .discussion-sidebar-shell is now position: fixed with insets of 1.25rem on top, right, and bottom. It uses width: var(--discussion-sidebar-width) and caps at calc(100vw - 2rem) on small screens.
The hidden-state transform changed from translateX(1rem) (a subtle nudge) to translateX(calc(100% + var(--discussion-sidebar-gap))), which fully slides the panel off-screen to the right.
Because the sidebar is no longer a descendant of the shell div, the open-state selector changed from a descendant selector (.branch-review-shell.discussion-open .discussion-sidebar-shell) to a CSS sibling combinator (.branch-page.discussion-open + .discussion-backdrop + .discussion-sidebar-shell). This relies on the new DOM order where <main>, <div.discussion-backdrop>, and <aside.discussion-sidebar-shell> are adjacent siblings.
The inner .discussion-panel switches from position: sticky (for scroll-pinning inside the grid) to position: relative; height: 100% with a prominent box-shadow, since the panel now fills its fixed-position container. The panel height also changes from a capped min(76vh, 880px) to a full 100%.
Flatten the HTML structure: remove wrapper div, hoist sidebar and backdrop
Intent: Remove the `#branch-review-shell` and `.branch-review-content` wrapper divs so that the review layout, diff section, discussion backdrop, and discussion sidebar are all direct children or siblings of `<main>`, enabling the new CSS sibling selectors.
Affected files: crates/pika-news/templates/detail.html
Evidence
@@ -453,7 +453,7 @@
{% block body %}
- <main class="branch-page">
+ <main id="branch-page" class="branch-page">
@@ -494,146 +498,142 @@
- <div id="branch-review-shell" class="branch-review-shell">
- <div class="branch-review-content">
- <div class="review-layout">
+ <div class="review-layout">
@@ -633,10 +633,10 @@
- <div id="discussion-backdrop" class="discussion-backdrop" hidden></div>
- <aside id="discussion-sidebar" class="discussion-sidebar-shell" aria-hidden="true">
- </div>
- </main>
+ </main>
+
+ <div id="discussion-backdrop" class="discussion-backdrop" hidden></div>
+ <aside id="discussion-sidebar" class="discussion-sidebar-shell" aria-hidden="true">
The template previously had this nesting:
<main>
<div#branch-review-shell>
<div.branch-review-content>
<div.review-layout> ... </div>
<section.diff-panel> ... </section>
</div>
<div#discussion-backdrop />
<aside#discussion-sidebar />
</div>
</main>
After this change:
<main#branch-page>
<div.review-layout> ... </div>
<section.diff-panel> ... </section>
</main>
<div#discussion-backdrop />
<aside#discussion-sidebar />
The two wrapper divs (#branch-review-shell and .branch-review-content) are removed. The <main> element gains id="branch-page" so JavaScript can reference it directly. The backdrop and sidebar are hoisted outside <main> entirely, making them siblings — which is required for the + sibling CSS selectors to work.
Update mobile responsive rules for the new DOM structure
Intent: Adjust the mobile media query breakpoint styles to reference the new class names and sibling selectors instead of the removed `.branch-review-shell`.
Affected files: crates/pika-news/templates/detail.html
Evidence
@@ -413,21 +417,21 @@
- .branch-review-shell,
- .branch-review-shell.discussion-open {
- display: block;
+ .branch-page.discussion-open {
+ max-width: none;
+ padding-right: 2rem;
}
.discussion-sidebar-shell {
position: fixed;
top: 0;
right: 0;
bottom: 0;
- width: min(92vw, 26rem);
+ width: min(94vw, 32rem);
padding: 1rem 0.75rem 1rem 0;
z-index: 40;
transform: translateX(100%);
}
- .branch-review-shell.discussion-open .discussion-sidebar-shell {
+ .branch-page.discussion-open + .discussion-backdrop + .discussion-sidebar-shell {
transform: translateX(0);
}
@@ -443,7 +447,7 @@
- .branch-review-shell.discussion-open .discussion-backdrop {
+ .branch-page.discussion-open + .discussion-backdrop {
display: block;
}
On mobile, the old rules set .branch-review-shell and its .discussion-open variant to display: block to collapse the grid. The new rules instead set .branch-page.discussion-open to max-width: none; padding-right: 2rem — removing the max-width constraint so content fills the screen.
The mobile sidebar width increases from min(92vw, 26rem) to min(94vw, 32rem), giving users a slightly wider drawer on small screens.
All mobile selectors for the sidebar and backdrop are updated to use the sibling combinator pattern (.branch-page.discussion-open + .discussion-backdrop + .discussion-sidebar-shell).
Update JavaScript to reference the new `branch-page` element ID
Intent: Replace all JS references from `branch-review-shell` to `branch-page` so the class toggle targets the `<main>` element.
Affected files: crates/pika-news/templates/detail.html
Evidence
@@ -720,7 +720,7 @@
- const reviewShell = document.getElementById('branch-review-shell');
+ const branchPage = document.getElementById('branch-page');
@@ -762,8 +762,8 @@
function setDiscussionOpen(nextOpen, options = {}) {
discussionOpen = !!nextOpen;
- if (reviewShell) {
- reviewShell.classList.toggle('discussion-open', discussionOpen);
+ if (branchPage) {
+ branchPage.classList.toggle('discussion-open', discussionOpen);
}
The JavaScript variable reviewShell (which pointed to #branch-review-shell) is renamed to branchPage and now references document.getElementById('branch-page'). The setDiscussionOpen function toggles the discussion-open class on this element, which triggers all the CSS transitions on <main> and, via sibling selectors, on the backdrop and sidebar.
Guard keyboard shortcuts against firing inside text inputs
Intent: Prevent the `[` and `]` review-navigation keyboard shortcuts from triggering when the user is typing in an input field, textarea, or contentEditable element — especially relevant now that the discussion input is always in the DOM as a fixed panel.
Affected files: crates/pika-news/templates/detail.html
Evidence
@@ -1155,6 +1155,15 @@
window.addEventListener('keydown', (event) => {
+ const target = event.target;
+ const typingTarget = target instanceof HTMLElement && (
+ target.tagName === 'INPUT' ||
+ target.tagName === 'TEXTAREA' ||
+ target.isContentEditable
+ );
+ if (typingTarget) {
+ return;
+ }
if (event.key === '[' && reviewPrevBtn && reviewPrevBtn.dataset.targetId) {
navigateReview(reviewPrevBtn.dataset.targetId);
}
A new guard is added at the top of the global keydown listener. It checks whether event.target is an INPUT, TEXTAREA, or contentEditable element. If so, the handler returns early without processing any shortcut keys.
This is particularly important because the discussion sidebar's text input is now always present in the DOM as a fixed-position element (rather than being hidden inside a collapsed grid column). Without this guard, typing [ or ] in the chat input would trigger review navigation.
Update Rust integration test for renamed element ID
Intent: Keep the server-side rendering test in sync with the template change from `#branch-review-shell` to `#branch-page`.
Affected files: crates/pika-news/src/web.rs
Evidence
@@ -8312,7 +8312,7 @@
- assert!(rendered.contains("id=\"branch-review-shell\""));
+ assert!(rendered.contains("id=\"branch-page\""));
The integration test at web.rs:8315 previously asserted that the rendered HTML contained id="branch-review-shell". Since that element was removed and the <main> tag now carries id="branch-page", the assertion is updated accordingly. This is the only back-end change in the branch — the rest is purely front-end template and CSS work.