diff --git a/frontend/src/components/gallery/GalleryLayout.tsx b/frontend/src/components/gallery/GalleryLayout.tsx index 46b23f22..bea030b9 100644 --- a/frontend/src/components/gallery/GalleryLayout.tsx +++ b/frontend/src/components/gallery/GalleryLayout.tsx @@ -40,6 +40,16 @@ interface GalleryLayoutProps { showDownloadAll?: boolean; onDownloadAll?: () => void; isDownloading?: boolean; + /** + * "Download" CTA shown immediately to the left of the Logout button in the + * standard / banner header. Same handler as Download All; the label and + * placement are intentionally simpler — single primary action right before + * Logout, the natural step at the end of a gallery visit (#386). Always + * visible when allowed (independent of sidebar state) so guests aren't + * forced to discover the download in the menu. + */ + showHeaderDownload?: boolean; + onHeaderDownload?: () => void; headerExtra?: React.ReactNode; menuButton?: React.ReactNode; headerStyle?: HeaderStyleType; @@ -54,6 +64,8 @@ export const GalleryLayout: React.FC = ({ showDownloadAll = false, onDownloadAll, isDownloading = false, + showHeaderDownload = false, + onHeaderDownload, headerExtra, menuButton, headerStyle: headerStyleProp, @@ -154,17 +166,22 @@ export const GalleryLayout: React.FC = ({
{/* Standard / Banner header - full bar with logo, event info, and actions (all layouts) */} {!isHeroHeader && !isMinimalHeader && !isNoHeader && ( -
+
+ {/* + * Menu icon is absolute-positioned at the very left of the header + * row instead of sitting inside the flex flow, so the logo's left + * edge can align with the leftmost gallery image (both anchored at + * `.container` left padding) — see #386. The icon stays vertically + * centred via top-1/2 + -translate-y-1/2. + */} + {menuButton && ( +
+ {menuButton} +
+ )}
- {/* Left side - Menu button, Logo */} -
- {/* Menu button */} - {menuButton && ( -
- {menuButton} -
- )} - + {/* Left side - Logo (menu lives in the absolute wrapper above) */} +
{/* Logo - Show custom logo or fallback to PicPeak logo */} {shouldShowLogo('header') && (
@@ -239,6 +256,27 @@ export const GalleryLayout: React.FC = ({ )} + {/* + * "Download" CTA — accent-coloured button immediately left of + * Logout, always visible when the gallery allows downloads. + * Uses var(--color-accent) inline so the same colour token + * shared with the 8-token palette (PR #400) resolves to the + * admin's chosen accent regardless of which PR merges first. + */} + {showHeaderDownload && onHeaderDownload && ( + + )} + {/* Logout button */} {showLogout && onLogout && ( )} + {/* Accent Download CTA — also rendered in the minimal header + so the action stays one click away regardless of header + style. Intentionally NOT shown in the no-header variant + where the gallery is fully chromeless by design. */} + {showHeaderDownload && onHeaderDownload && ( + + )} {showLogout && onLogout && ( )} + {/* Accent Download CTA — also rendered above the hero so the + primary download action is reachable without scrolling. + Intentionally NOT shown in the no-header variant where + the gallery is fully chromeless by design. */} + {showHeaderDownload && onHeaderDownload && ( + + )} + {/* Logout button */} {showLogout && onLogout && ( - ) : undefined} + menuButton={ + // Menu icon is shown when the event theme uses the sidebar + // controls style. The button is icon-only — the redundant + // "Menu" text label was dropped (#386). The wrapper aligns the + // icon to the very left of the header so the logo lines up + // with the leftmost gallery image. + showSidebar ? ( + + ) : undefined + } + showHeaderDownload={allowDownloads} + onHeaderDownload={handleDownloadAll} headerExtra={(() => { const items = [];