From 5e18c9d5019b1c72a4e4293e11a4bb6632f11d1b Mon Sep 17 00:00:00 2001 From: julioest Date: Wed, 24 Jun 2026 13:09:45 -0400 Subject: [PATCH 1/2] Refactor v3 hero into one tokenized component Merges the home, community, and library/release heroes into a single .hero base with per-page modifiers and one heading style, splits the mascot foreground from the background so each scales independently across breakpoints, and drives all the responsive sizing through tokens set once per tier. Hero image URLs move out of the templates and into the page views, and the duplicated absolute-header rules collapse into one body.v3 .header rule. Also brings the hero link buttons' line-height and letter-spacing to the Figma spec, which closes the remaining open item on #2367. --- ak/views.py | 19 +- core/mock_data.py | 4 + core/views.py | 12 + static/css/v3/auth-page.css | 8 - static/css/v3/buttons.css | 4 + static/css/v3/community.css | 10 +- static/css/v3/header.css | 13 + static/css/v3/heros.css | 1114 ++++++++++------- static/css/v3/library-subpage.css | 6 - static/css/v3/release-detail.css | 32 +- static/css/v3/v3-homepage.css | 24 - templates/v3/community.html | 3 +- .../v3/examples/_v3_example_section.html | 2 +- templates/v3/homepage.html | 2 +- templates/v3/includes/_hero_home.html | 27 +- templates/v3/includes/_hero_library.html | 55 +- templates/v3/release_detail.html | 2 +- versions/views.py | 12 + 18 files changed, 742 insertions(+), 607 deletions(-) diff --git a/ak/views.py b/ak/views.py index 6f1e2b720..da8d4cb2c 100644 --- a/ak/views.py +++ b/ak/views.py @@ -186,11 +186,20 @@ def get_v3_context_data(self, **kwargs): ctx["build_anything_with_boost"] = SharedResources.build_anything_with_boost - ctx["hero_legacy_image_url_light"] = SharedResources.hero_legacy_image_url_light - ctx["hero_legacy_image_url_dark"] = SharedResources.hero_legacy_image_url_dark - ctx["hero_image_url"] = SharedResources.hero_image_url - ctx["hero_image_url_light"] = SharedResources.hero_image_url_light - ctx["hero_image_url_dark"] = SharedResources.hero_image_url_dark + # Hero foreground/background swapped with the community hero (see + # CommunityView): home now shows the community scene and vice versa. + home_foreground = large_static("img/v3/community-page/community-foreground.png") + ctx["hero_image_url"] = home_foreground + ctx["hero_image_url_light"] = home_foreground + ctx["hero_image_url_dark"] = home_foreground + # Mobile art-direction: a tightly-trimmed foreground so it isn't letterboxed + # on narrow screens. Desktop/tablet keep the wide foreground above. + ctx["hero_image_url_mobile"] = large_static( + "img/v3/community-page/community-foreground-mobile.png" + ) + ctx["hero_background_image_url"] = large_static( + "img/v3/community-page/community-background.png" + ) return ctx diff --git a/core/mock_data.py b/core/mock_data.py index 50e963862..b71ea6473 100644 --- a/core/mock_data.py +++ b/core/mock_data.py @@ -424,6 +424,10 @@ class SharedResources: hero_image_url_light = large_static("img/v3/home-page/home-page-foreground.png") hero_image_url_dark = large_static("img/v3/home-page/home-page-foreground.png") + hero_background_image_url = large_static( + "img/v3/home-page/home-page-background.png" + ) + library_about_code = ( "int main()\n" "{\n" diff --git a/core/views.py b/core/views.py index ec9798a68..e3097b58f 100644 --- a/core/views.py +++ b/core/views.py @@ -347,6 +347,18 @@ def get_v3_context_data(self, **kwargs): ctx["archive_url"] = ( "https://lists.boost.org/archives/list/boost@lists.boost.org/latest" ) + # Hero foreground/background swapped with the home hero (see HomeView): + # community now shows the home scene and vice versa. + ctx["hero_image_url"] = large_static( + "img/v3/home-page/home-page-foreground.png" + ) + # Mobile art-direction: a tighter crop swapped in via at <=767px. + ctx["hero_image_url_mobile"] = large_static( + "img/v3/home-page/home-page-foreground-mobile.png" + ) + ctx["hero_background_image_url"] = large_static( + "img/v3/home-page/home-page-background.png" + ) return ctx diff --git a/static/css/v3/auth-page.css b/static/css/v3/auth-page.css index fce53cccc..340289dfd 100644 --- a/static/css/v3/auth-page.css +++ b/static/css/v3/auth-page.css @@ -5,14 +5,6 @@ Dependencies: foundations, forms, buttons. */ -/* ── Header overlay on auth pages ─────────── */ -body:has(.auth-page) .header { - position: absolute; - z-index: 10; - left: 0; - right: 0; -} - /* ── Page-level flex column ───────────────── */ .auth-page { display: flex; diff --git a/static/css/v3/buttons.css b/static/css/v3/buttons.css index 4c33da5e0..d42db4444 100644 --- a/static/css/v3/buttons.css +++ b/static/css/v3/buttons.css @@ -231,6 +231,10 @@ font-family: var(--font-sans); font-size: var(--font-size-small); font-weight: var(--font-weight-medium); + /* Pin line-height + letter-spacing to the Figma button spec (17.36px / -0.14px + at the 14px size) instead of inheriting the page defaults (16.8px / -0.16px). */ + line-height: var(--line-height-relaxed); + letter-spacing: var(--letter-spacing-tight); color: var(--color-text-primary); background-color: var(--color-surface-weak); border: 1px solid var(--color-stroke-weak); diff --git a/static/css/v3/community.css b/static/css/v3/community.css index 7f2d8ad6f..0ae90195d 100644 --- a/static/css/v3/community.css +++ b/static/css/v3/community.css @@ -3,14 +3,8 @@ * BEM naming, semantic tokens with fallbacks. */ -/* ── Navbar overlays hero (matches homepage pattern) ──────────── */ - -body:has(.community-v3) .header { - position: absolute; - z-index: 10; - left: 0; - right: 0; -} +/* Hero styling (including the full-bleed .hero-fg foreground) lives in heros.css + under `.hero--community`, set via the template's extra_classes. */ /* ── Page container (below full-bleed hero) ───────────────────── */ diff --git a/static/css/v3/header.css b/static/css/v3/header.css index 3452294df..92e0a5316 100644 --- a/static/css/v3/header.css +++ b/static/css/v3/header.css @@ -22,6 +22,19 @@ color: var(--color-text-primary); } +/* Consistent header positioning on all v3 pages. + Header overlays the top of the page; non-hero pages reserve space below. */ +body.v3 .header { + position: absolute; + z-index: 10; + left: 0; + right: 0; +} + +body.v3 .v3-container > .w-full { + padding-top: calc(var(--header-height, 48px) + 2 * var(--space-medium)); +} + /* ── Shared control surface ────────────────────── */ .header__nav--desktop, diff --git a/static/css/v3/heros.css b/static/css/v3/heros.css index e3b094b8e..b194c931a 100644 --- a/static/css/v3/heros.css +++ b/static/css/v3/heros.css @@ -1,624 +1,770 @@ /** - * Heroes – Home and Library hero sections. - * All classes are hero-specific (heros__, hero-home__, hero-library__). + * Heroes – one unified .hero component: a shared base/banner plus a .hero--home variant. + * All classes are hero-specific (heros__, hero__, hero--home / hero--community / hero--release). * Reuses: .btn, .btn-hero, .btn-primary, .btn-secondary, .btn-icon-library (foundations/buttons), * .category-tag, .version-tag (tokens/category-tags.css). + * + * Layout: 1) Tokens 2) Shared 3) Hero home variant 4) Hero (base/banner) 5) Hero variants. + * Within each hero, base rules come first, then media queries in ascending + * breakpoint order (tablet ≤1279, then mobile ≤767), so the cascade reads top-down. */ - .heros, - .heros * { - box-sizing: border-box; - } +/* ═══════════════════════════════════════════════════════════════════════════ + 1. TOKENS — hero positioning. + Foreground SIZE + SHIFT + mobile ASPECT are SHARED by every hero: + --hero-fg-width / -height / -shift-x / -aspect are a single set, so home and + community frame identically (the same code matches; drop-in for on-spec art per + ILLUSTRATOR-HERO-SPEC). Only per-hero anchoring (--hero-fg-*-margin-*) stays + per hero. Backgrounds keep a per-type focal point. + home = .hero--home, community = .hero--community. + + A) CONTRACT — the shared, art-agnostic frame: foreground fills the block, + bottom-anchored, no nudge; background covers low. Do NOT tune per page. + B) CURRENT-ART OVERRIDES — what's still pinned to today's off-spec art + (per-hero anchoring, background focal/scale, mobile crop ratios). Delete + when on-spec art ships. + + Block height is layout (Figma 480/400/600), in the contract with its own steps. + ═══════════════════════════════════════════════════════════════════════════ */ +/* ── A) CONTRACT (shared frame, keep) ─────────────────────────────────────── */ +:root { + /* Shared foreground frame — home + community must match */ + --hero-fg-inset-block: 0; + --hero-fg-width: 100%; + --hero-fg-height: 100%; + --hero-fg-shift-x: 0; + + /* Shared mobile crop ratio — home + community match (on-spec canvas, used at <=767) */ + --hero-fg-aspect: 768 / 672; + + /* Shared anchoring — full-width, centered (home + community match) */ + --hero-fg-margin-start: auto; + --hero-fg-margin-end: auto; + + /* Backgrounds: cover + per-type focal point (anchored low per spec) */ + --hero-bg-home-size: cover; + --hero-bg-home-position-x: center; + --hero-bg-home-position-y: 80%; + --hero-bg-community-size: cover; + --hero-bg-community-position-x: center; + --hero-bg-community-position-y: 90%; + + /* Block height — layout, not art (Figma 480/400/600). Stays after override removal. */ + --hero-block-height: 480px; +} +@media (max-width: 1279px) { + :root { --hero-block-height: 400px; } +} +@media (max-width: 767px) { + :root { --hero-block-height: 600px; } +} - /* Wrapper layout and doc header use same inline styles as stats.stories.tsx. */ +/* ── B) CURRENT-ART OVERRIDES — remove when on-spec art ships ──────────────── */ +/* Foreground size/shift are now shared (above); what remains pinned to today's + art is per-hero anchoring, background focal/scale, and the mobile crop ratios. */ +@media (max-width: 1279px) { + :root { + /* Tablet */ + --hero-bg-home-position-x: 100%; + --hero-bg-community-size: max(1200px, 100%); + --hero-bg-community-position-x: 100%; + } +} +@media (max-width: 767px) { + :root { + /* Mobile — focal points, height-driven bg. */ + --hero-bg-home-position-x: 80%; + --hero-bg-home-position-y: top; + --hero-bg-community-position-x: 87%; + --hero-bg-community-position-y: bottom; + --hero-bg-community-size: auto 120%; + } +} - /* Doc header uses same inline styles as stats.stories / other component docs – no hero-specific classes. */ +/* ═══════════════════════════════════════════════════════════════════════════ + 2. SHARED + ═══════════════════════════════════════════════════════════════════════════ */ +.heros, +.heros * { + box-sizing: border-box; +} - /* ----- Hero home ----- */ - .hero-home { - display: flex; - flex-direction: column; +/* ═══════════════════════════════════════════════════════════════════════════ + 3. HERO — HOME VARIANT (.hero.hero--home) + Shares the base .hero section plus the --no-image / --with-background-image + modifiers (defined under HERO below). Only the home-specific split layout + (content + install card) and its block/content overrides live here. + ═══════════════════════════════════════════════════════════════════════════ */ +.hero.hero--home { + display: flex; + flex-direction: column; gap: var(--space-large); - width: 100%; - justify-content: space-between; - align-items: center; + width: 100%; + justify-content: space-between; + align-items: center; background-image: var(--hero-bg-image); background-repeat: no-repeat; - background-position: center center; - background-size: min(1440px, 100%) 100%; + background-position: var(--hero-bg-home-position-x) var(--hero-bg-home-position-y); + background-size: var(--hero-bg-home-size); background-color: var(--color-surface-weak-accent-yellow); - margin-bottom: var(--space-large, 16px); - } - -/* If a background image is provided, surface color should be transparent */ -.hero-home--with-background-image { - background-color: transparent; + margin-bottom: var(--space-large, 16px); } - .dark .hero-home { - background-color: #081040; - } - -.dark .hero-home--with-background-image { - background-color: transparent; +/* Home block: split layout (content + install card). Scoped under .hero--home so + the base .hero__block (library banner) rules don't apply; z-index/justify-content + are reset to neutralize the base block's values that would otherwise leak in. */ +.hero--home .hero__block { + width: 100%; + max-width: 1440px; + height: 480px; + position: relative; + display: flex; + flex-direction: row; + padding: calc(var(--space-medium) + var(--space-default)); + align-items: end; + padding-top: 60px; + z-index: auto; + justify-content: normal; } - .hero-home--no-image { - background-image: none; - background: none; - } - - .dark .hero-home--no-image { - background: none; - } - - .hero-home__block { - width: 100%; - max-width: 1440px; - min-height: 480px; - position: relative; - display: flex; - align-items: center; - flex-direction: row; - padding: calc(var(--space-medium) + var(--space-default)); - align-items: end; - padding-top: 60px; - } +.hero--home .hero__content { + display: flex; + flex-direction: column; + gap: var(--space-xl); + align-items: flex-start; + max-width: 950px; + flex: 1; + z-index: 1; + align-self: center; + margin-left: 0; +} -.hero-home--no-image .hero-home__block { - align-items: center; +/* Headline forces three lines at every tier, with different break points: + desktop/tablet break after "the" and "community."; mobile breaks after "C++" and "by". */ +.hero__title-br--mobile { + display: none; } - .hero-home__content { - display: flex; - flex-direction: column; - gap: var(--space-xl); - align-items: flex-start; - left: var(--space-large); - top: calc(2 * var(--space-xxl)); - max-width: 950px; - margin: auto; - margin-left: 0; - width: 53%; - } - - .hero-home__headline { - color: var(--color-text-primary); - font-size: var(--font-size-3xl); - line-height: var(--line-height-tight); - font-weight: var(--font-weight-regular); - margin: 0; - align-self: stretch; - letter-spacing: -4px; - z-index: 1; - } - - /* When hero has background + center image, main headline is white (light and dark mode) */ - .hero-home--with-bg-and-image .hero-home__headline { - color: #fff; - } - - .hero-home__actions { - display: flex; - flex-direction: row; +.hero__actions { + display: flex; + flex-direction: row; gap: var(--space-default); - align-items: flex-start; - } + align-items: flex-start; +} - .btn.btn-hero.btn-secondary { - background-color: var(--color-surface-weak); - border-color: var(--color-stroke-weak); - } +.btn.btn-hero.btn-secondary { + background-color: var(--color-surface-weak); + border-color: var(--color-stroke-weak); +} - .hero-home__actions .btn-icon { - flex-shrink: 0; +.hero__actions .btn-icon { + flex-shrink: 0; width: var(--space-large); height: var(--space-large); - } - - .hero-home__card { - z-index: 1; - min-width: 458px; - } - - .hero-home__image { - width: 488px; - max-width: calc(100vw - var(--space-xl)); - height: 416px; - position: absolute; - left: 50%; - transform: translateX(-50%); - bottom: 0; - overflow: hidden; - } - -/* Hide card on tablet and mobile; only show on desktop */ - @media (max-width: 1279px) { - .hero-home__content { - width: 100%; - } - - .hero-home__card { - display: none; - } - - .hero-home__image{ - position: relative; - transform: none; - left: 0; - } - } - - .hero-home__card-title { - color: var(--color-text-primary); - font-family: var(--font-display); +} + +.hero__card { + z-index: 1; + min-width: 458px; +} + +.hero__card-title { + color: var(--color-text-primary); + font-family: var(--font-display); font-size: var(--font-size-large); line-height: var(--line-height-tight); - letter-spacing: -0.01em; + letter-spacing: -0.01em; font-weight: var(--font-weight-medium); - margin: 0; - align-self: stretch; - } + margin: 0; + align-self: stretch; +} - .hero-home__card-body { - display: flex; - flex-direction: column; +.hero__card-body { + display: flex; + flex-direction: column; gap: var(--space-medium); - align-self: stretch; - } + align-self: stretch; +} - .hero-home__tabs { - display: flex; - flex-direction: row; +.hero__tabs { + display: flex; + flex-direction: row; gap: var(--space-medium); - } +} - .hero-home__tab { +.hero__tab { padding-bottom: var(--space-xs); - } +} - .hero-home__tab--active { - border-bottom: 1px solid var(--color-text-secondary); - } +.hero__tab--active { + border-bottom: 1px solid var(--color-text-secondary); +} - .hero-home__tab span { - color: var(--color-text-primary); +.hero__tab span { + color: var(--color-text-primary); font-size: var(--font-size-xs); - line-height: 1.2; + line-height: 1.2; font-weight: var(--font-weight-regular); - } +} - .hero-home__tab:not(.hero-home__tab--active) span { - color: var(--color-text-tertiary); - } +.hero__tab:not(.hero__tab--active) span { + color: var(--color-text-tertiary); +} - .hero-home__code-row { - background: var(--color-surface-mid); +.hero__code-row { + background: var(--color-surface-mid); border-radius: var(--border-radius-l); padding: var(--space-medium) var(--space-large); - display: flex; - flex-direction: row; + display: flex; + flex-direction: row; gap: var(--space-xl); - align-items: center; - align-self: stretch; - } + align-items: center; + align-self: stretch; +} - .hero-home__code-text { - color: var(--color-text-primary); - font-family: var(--font-mono, monospace); +.hero__code-text { + color: var(--color-text-primary); + font-family: var(--font-mono, monospace); font-size: var(--font-size-xs); line-height: var(--line-height-code); - flex: 1; - } + flex: 1; +} - .hero-home__code-badge { - background: var(--color-surface-strong); +.hero__code-badge { + background: var(--color-surface-strong); border-radius: var(--border-radius-l); padding: var(--space-default); - display: flex; - flex-direction: row; + display: flex; + flex-direction: row; gap: var(--space-default); - align-items: center; - } + align-items: center; +} - .hero-home__code-badge-label { - color: var(--color-text-primary); +.hero__code-badge-label { + color: var(--color-text-primary); font-size: var(--font-size-xs); line-height: var(--line-height-tight); font-weight: var(--font-weight-regular); - } - - - .hero-home__image .hero-home__img { - width: 100%; - height: 100%; - object-fit: contain; - object-position: center bottom; - display: block; - } - - .hero-home__image--theme-aware .hero-home__img--light { - display: block; - } - .hero-home__image--theme-aware .hero-home__img--dark { - display: none; - } - .dark .hero-home__image--theme-aware .hero-home__img--light { - display: none; - } - .dark .hero-home__image--theme-aware .hero-home__img--dark { - display: block; - } - - [data-theme="dark"] .hero-home__image .hero-home__img { - mix-blend-mode: screen; - } - - /* ----- Hero home: mobile ----- */ - @media (max-width: 1000px) { - .hero-home__actions{ - flex-direction: column; - } - - } - - /* ----- Hero home: mobile ----- */ - @media (max-width: 767px) { - .hero-home__block { - height: auto; - min-height: 600px; - display: flex; - flex-direction: column; - align-items: stretch; - padding: calc(5 * var(--space-large)) var(--space-default) 0 var(--space-default); - } +} - .hero-home--no-image .hero-home__block { - min-height: auto; +/* ─────────────────────────────────────────────────────────────────────────── + Foreground frame — SHARED by the home hero and the community hero (full-bleed, + bottom-anchored, baked-in-position art). Applied via class `hero-fg` in both + _hero_home.html and _hero_library.html (the latter gated by `fullbleed_fg`). + Library-detail keeps its own small-box `.hero__image` below. + ─────────────────────────────────────────────────────────────────────────── */ +.hero-fg { + position: absolute; + inset-block-end: var(--hero-fg-inset-block); + inset-inline: 0; + margin-inline-start: var(--hero-fg-margin-start); + margin-inline-end: var(--hero-fg-margin-end); + transform: translateX(var(--hero-fg-shift-x, 0)); + width: var(--hero-fg-width); + max-width: 100%; + height: var(--hero-fg-height); + overflow: hidden; +} + +/* is only a per-breakpoint source swap; keep it transparent to layout + so the img still sizes against .hero-fg. */ +.hero-fg picture { + display: contents; +} + +.hero-fg__img { + width: 100%; + height: 100%; + object-fit: contain; + object-position: center bottom; + display: block; +} + +.hero-fg--theme-aware .hero-fg__img--light { + display: block; +} +.hero-fg--theme-aware .hero-fg__img--dark { + display: none; +} +.dark .hero-fg--theme-aware .hero-fg__img--light { + display: none; +} +.dark .hero-fg--theme-aware .hero-fg__img--dark { + display: block; +} + +[data-theme="dark"] .hero-fg .hero-fg__img { + /* CURRENT-ART dark fallback; replace with explicit on-spec dark exports (spec §4). */ + mix-blend-mode: screen; +} + +/* ----- Hero home: tablet ----- */ +/* Hide card on tablet and mobile; only show on desktop. */ +@media (max-width: 1279px) { + .hero--home .hero__block { + height: 360px; + } + + .hero__card { + display: none; } +} - .hero-home { - background-size: cover; - background-position: center top; +/* ----- Hero home: actions stack below 1000 ----- */ +@media (max-width: 1000px) { + .hero__actions { + flex-direction: column; } +} - .hero-home__content { - position: static; - left: auto; - top: auto; - width: 100%; - max-width: 100%; +/* ----- Hero home: mobile ----- */ +@media (max-width: 767px) { + .hero--home .hero__block { + height: auto; + /* Figma mobile hero is 600px. The figure is pinned to the bottom (margin-top:auto + on .hero-fg), so on narrow screens the slack sits ABOVE the figure + (filled by the background) rather than below it (which read as "floating"). */ + min-height: 600px; + display: flex; + flex-direction: column; + align-items: stretch; + padding: calc(5 * var(--space-large)) var(--space-default) 0 var(--space-default); + overflow: hidden; + } + + .hero--home .hero__content { + position: static; + left: auto; + top: auto; + width: 100%; + max-width: 100%; + margin-left: 0; gap: var(--space-xl); - order: 1; - align-self: stretch; - } + order: 0; + align-self: stretch; + /* No grow on mobile: a flex-grow sibling would absorb the figure's negative + margin (consuming the freed space), neutralizing the pull. */ + flex: none; + } - .hero-home__headline { - font-size: var(--font-size-3xl); - line-height: var(--line-height-tight); - letter-spacing: 0px; - } - - .hero-home__actions { - flex-direction: column; - align-items: stretch; - align-self: stretch; - width: 100%; + .hero__title-br { + display: none; + } + .hero__title-br--mobile { + display: inline; + } + + .hero__actions { + flex-direction: column; + align-items: stretch; + align-self: stretch; + width: 100%; gap: var(--space-default); - } - - .hero-home__actions .btn { - width: 100%; - min-width: 0; - justify-content: center; - box-sizing: border-box; - } - - .hero-home__image { - position: static; - left: auto; - transform: none; - bottom: auto; - width: 100%; - max-width: calc(100vw - var(--space-large)); - height: 306px; - margin: var(--space-xlarge) auto 0; - order: 2; - flex-shrink: 0; - } - } - - /* ----- Hero library ----- */ - .hero-library { - display: flex; - flex-direction: column; + } + + .hero__actions .btn { + width: 100%; + min-width: 0; + min-height: 0; + justify-content: center; + box-sizing: border-box; + } + + /* Pin the figure to the bottom of the block: margin-top:auto absorbs the free + space above it (filled by the background) so it stays grounded when + content+figure is shorter than the block, and is a no-op once they exceed it. + Shared by home + community (both go in-flow and bottom-anchored on mobile). */ + .hero-fg { + position: relative; + margin-top: auto; + margin-inline: 0; + aspect-ratio: var(--hero-fg-aspect); + height: auto; + } + + /* Overlay darkens the top edge (for header + headline legibility) instead of + the left edge used on wider screens. */ + .hero--home.hero--with-background-image::before { + background: linear-gradient(360deg, rgba(0, 0, 0, 0) 26.53%, rgba(0, 0, 0, 0.5) 100%); + } +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 4. HERO — BASE / SHARED BANNER (.hero) + The default hero used by library detail, community, and release pages. + ═══════════════════════════════════════════════════════════════════════════ */ +.hero { + display: flex; + flex-direction: column; gap: var(--space-large); - width: 100%; + width: 100%; background-image: var(--hero-bg-image); background-repeat: no-repeat; background-position: center center; - background-size: min(1440px, 100%) 100%; + background-size: var(--hero-bg-community-size); background-color: var(--color-surface-weak-accent-yellow); - align-items: center; - } + align-items: center; +} /* If a background image is provided, surface color should be transparent */ -.hero-library--with-background-image { - background-color: transparent; +.hero--with-background-image { + background-color: transparent; + position: relative; } - .dark .hero-library { +.hero--with-background-image::before { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient(90deg, rgba(0, 0, 0, 0.70) 0%, rgba(0, 0, 0, 0.00) 73.12%); + z-index: 0; +} + +.dark .hero { background-color: #081040; - } - -.dark .hero-library--with-background-image { - background-color: transparent; -} - - .hero-library--no-image { - background-image: none; - background: none; - } - - .dark .hero-library--no-image { - background: none; - } - - .hero-library__block { - width: 100%; - max-width: 1440px; - display: flex; - flex-direction: column; - gap: var(--space-large); - padding-bottom: var(--space-avatar); - padding-top: var(--space-hero-padding-top); - } - - .hero-library__main { - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - } - -.hero-library--no-image .hero-library__main { +} + +.dark .hero--with-background-image { + background-color: transparent; +} + +.hero--no-image { + background: none; +} + +.dark .hero--no-image { + background: none; +} + +.hero__block { + width: 100%; + max-width: 1440px; + height: var(--hero-block-height); + position: relative; + z-index: 1; + display: flex; align-items: center; + justify-content: space-between; + padding-bottom: var(--space-avatar); + padding-top: var(--space-hero-padding-top); } -/* Banner sits inside __block; inset its sides to match content/image margins */ -.hero-library__block > .banner { - margin: 0 var(--space-large); - width: auto; +.hero--no-image .hero__block { + align-items: center; } - .hero-library__content { - display: flex; - flex-direction: column; +.hero__content { + display: flex; + flex-direction: column; gap: var(--space-xlarge); margin-left: var(--space-large); - max-width: 662px; - flex: 1; - } + max-width: 662px; + flex: 1; +} - .hero-library__meta { - display: flex; - flex-direction: column; +.hero__meta { + display: flex; + flex-direction: column; gap: var(--space-large); - align-self: stretch; - } + align-self: stretch; +} - .hero-library__tags { - display: flex; - flex-direction: row; +.hero__tags { + display: flex; + flex-direction: row; gap: var(--space-xs); - align-items: center; - flex-wrap: wrap; - } + align-items: center; + flex-wrap: wrap; +} - .hero-library__added-wrap { - display: inline-flex; - align-items: center; +.hero__added-wrap { + display: inline-flex; + align-items: center; padding: calc(3 * var(--space-xs)) var(--space-default); - border-radius: 1000px; - } + border-radius: 1000px; +} - .hero-library__added { - color: var(--color-text-secondary); +.hero__added { + color: var(--color-text-secondary); font-size: var(--font-size-xs); - line-height: 1.2; + line-height: 1.2; font-weight: var(--font-weight-regular); - } +} /* Keep added text color consistent with description on bg+image heroes */ -.hero-library--with-bg-and-image .hero-library__added { - color: var(--color-primary-grey-400); +.hero--with-bg-and-image .hero__added { + color: var(--color-primary-grey-400); } - .hero-library__text-block { - display: flex; - flex-direction: column; +.hero__text-block { + display: flex; + flex-direction: column; gap: var(--space-medium); - align-self: stretch; - } - - .hero-library__title { - color: var(--color-text-primary); - font-size: var(--font-size-2xl); - line-height: 1; - letter-spacing: -0.01em; - font-weight: 400; - } - - .hero-library__title--flagship { - font-size: var(--font-size-3xl); - } - - /* When hero has background + center image, main title is white (light and dark mode) */ - .hero-library--with-bg-and-image .hero-library__title { - color: #fff; - } - - /* When hero has background + center image, description uses secondary text color (light and dark mode) */ - .hero-library--with-bg-and-image .hero-library__description { - color: var(--color-primary-grey-400); - } - - .hero-library__description { - color: var(--color-text-secondary); + align-self: stretch; +} + +/* The single hero heading, identical on every page (home, community, library, + release). One size ladder: 64px desktop, 40px tablet, 32px mobile (steps below). */ +.hero__title { + color: var(--color-text-primary); + font-size: var(--font-size-3xl); + line-height: var(--line-height-tight); + font-weight: var(--font-weight-regular); + letter-spacing: -4px; + margin: 0; + align-self: stretch; + z-index: 1; +} + +/* When hero has background + center image, main title is white (light and dark mode) */ +.hero--with-bg-and-image .hero__title { + color: #fff; +} + +/* When hero has background + center image, description uses secondary text color (light and dark mode) */ +.hero--with-bg-and-image .hero__description { + color: var(--color-primary-grey-400); +} + +.hero__description { + color: var(--color-text-secondary); font-size: var(--font-size-large); - line-height: 1.33; - letter-spacing: -0.01em; + line-height: 1.33; + letter-spacing: -0.01em; font-weight: var(--font-weight-regular); - margin: 0; - padding: 0; - } + margin: 0; + padding: 0; +} - .hero-library__links { - display: flex; - flex-direction: row; +.hero__links { + display: flex; + flex-direction: row; gap: var(--space-s); - flex-wrap: wrap; - align-self: stretch; - } + flex-wrap: wrap; + align-self: stretch; +} - .hero-library__links .btn-icon-library { - height: 32px; +.hero__links .btn-icon-library { + height: 32px; padding: var(--space-default) var(--space-large); - } +} + +/* In dark mode the hero links sit over the photo, so they use Surface-Mid (#2F313D, + Figma) instead of the global button's Surface-Weak (#1A1C29). The hover rule is + repeated here only because this resting rule would otherwise out-specify the + global dark hover and freeze the hover state. */ +html.dark .hero__links .btn-icon-library { + background-color: var(--color-surface-mid); +} - .hero-library__links .btn-icon { +html.dark .hero__links .btn-icon-library:hover { + background-color: var(--color-stroke-mid); +} + +.hero__links .btn-icon { width: var(--space-large); height: var(--space-large); - } +} - .hero-library__image { - width: 488px; - height: 416px; +.hero__image { + width: 488px; + height: 416px; margin-right: var(--space-large); - overflow: hidden; - flex: 1; - display: flex; - flex-direction: row-reverse; - } - - .hero-library__image .hero-library__img { - height: 100%; - object-fit: contain; - object-position: center bottom; - display: block; - } - - .hero-library__image--theme-aware .hero-library__img--light { - display: block; - } - .hero-library__image--theme-aware .hero-library__img--dark { - display: none; - } - .dark .hero-library__image--theme-aware .hero-library__img--light { - display: none; - } - .dark .hero-library__image--theme-aware .hero-library__img--dark { - display: block; - } - - [data-theme="dark"] .hero-library__image .hero-library__img { - mix-blend-mode: screen; - } - - /* ----- Hero library: mobile ----- */ - @media (max-width: 767px) { - - .hero-library__block { - padding: 96px var(--space-default) 32px var(--space-default); - } - - .hero-library__main { - min-height: 210px; - flex-direction: column; - justify-content: center; - } - - .hero-library--no-image .hero-library__main { - min-height: auto; - height: auto; + overflow: hidden; + flex: 1; + display: flex; + flex-direction: row-reverse; +} + +/* is only a per-breakpoint source swap; keep it transparent to layout + so the img still sizes against .hero__image. */ +.hero__image picture { + display: contents; +} + +.hero__image .hero__img { + height: 100%; + object-fit: contain; + object-position: center bottom; + display: block; +} + +.hero__image--theme-aware .hero__img--light { + display: block; +} +.hero__image--theme-aware .hero__img--dark { + display: none; +} +.dark .hero__image--theme-aware .hero__img--light { + display: none; +} +.dark .hero__image--theme-aware .hero__img--dark { + display: block; +} + +[data-theme="dark"] .hero__image .hero__img { + /* CURRENT-ART dark fallback; replace with explicit on-spec dark exports (spec §4). */ + mix-blend-mode: screen; +} + +/* ----- Hero base: tablet ----- */ +/* Step the heading down from 64px to 40px. */ +@media (max-width: 1279px) { + .hero__title { + font-size: var(--font-size-2xl); + letter-spacing: -2px; } +} - .hero-library__block > .banner { - margin: 0; +/* ----- Hero base: mobile ----- */ +@media (max-width: 767px) { + .hero__block { + height: auto; + min-height: var(--hero-block-height); + flex-direction: column; + justify-content: center; + padding: 96px var(--space-default) 32px var(--space-default); } - .hero-library { - background-size: cover; - background-position: center top; + .hero--no-image .hero__block { + min-height: auto; + height: auto; } - .hero-library__content { - margin: 0; - margin-right: auto; - flex: none; - } + .hero__content { + margin: 0; + margin-right: auto; + flex: none; + } - .hero-library__meta { + .hero__meta { gap: var(--space-medium); - } + } - .hero-library__tags .category-tag { + .hero__tags .category-tag { padding: var(--space-default); border-radius: var(--border-radius-s); font-size: var(--font-size-xs); line-height: var(--line-height-tight); - } + } - .hero-library__tags .version-tag { + .hero__tags .version-tag { padding: var(--space-default); border-radius: var(--border-radius-l); font-size: var(--font-size-xs); line-height: var(--line-height-tight); - } + } - .hero-library__added { + .hero__added { font-size: var(--font-size-xs); - line-height: 1.2; - } + line-height: 1.2; + } - .hero-library__text-block { + .hero__text-block { gap: var(--space-default); - } - - .hero-library__title { - font-size: var(--font-size-2xl); - } + } - .hero-library__title--flagship { + .hero__title { font-size: var(--font-size-3xl); - } + letter-spacing: 0px; + } - .hero-library__description { + .hero__description { font-size: var(--font-size-large); line-height: var(--line-height-loose-alt); - } + } - .hero-library__links .btn-icon-library { + .hero__links .btn-icon-library { padding: var(--space-default) 12px; gap: var(--space-default); border-radius: var(--border-radius-l); font-size: var(--font-size-small); line-height: var(--line-height-relaxed); - height: 32px; - } + height: 32px; + } - .hero-library__links .btn-icon { - width: 16px; - height: 16px; - } + .hero__links .btn-icon { + width: 16px; + height: 16px; + } - .hero-library__image { + .hero__image { width: min(100%, calc(100vw - var(--space-large))); margin-top: var(--space-large); - margin-right: 0px; - height: 306px; - right: 0px; - bottom: 0; - justify-content: center; - } - } + margin-right: 0px; + height: 306px; + right: 0px; + bottom: 0; + justify-content: center; + } +} + +/* ═══════════════════════════════════════════════════════════════════════════ + 5. HERO VARIANTS (page-specific) + Modifier classes set by the template via `extra_classes`, so page stylesheets + no longer reach into hero internals. + --community community page: shares the full-bleed .hero-fg foreground (home) + --release release detail: solid surface, taller block, no foreground + ═══════════════════════════════════════════════════════════════════════════ */ + +/* Community: background focal point (independent of other library heroes) */ +.hero--community { + background-position: var(--hero-bg-community-position-x) var(--hero-bg-community-position-y); +} + +/* Community foreground uses the shared `.hero-fg` frame (gated by fullbleed_fg in + _hero_library.html) — no community-specific foreground rules needed. */ + +.hero--community .hero__content { + z-index: 1; + /* Narrower than the base 662px so the headline wraps. Shrinks further in the + tablet band (see below) so it clears the full-bleed mascot. */ + max-width: 615px; +} + +/* Subtext white over the hero scene (Figma: Text-Reversed-on-Accent, #FFF), same + as the title and home headline; overrides the grey-400 other library heroes use. + Literal #fff (not --color-text-reversed, which is black in light mode) so it + stays white in both themes over the dark background photo. The remaining type + (24px / 1.33 / -0.01em ≈ -0.24px / 400) already matches Figma via the base rule. */ +.hero--community .hero__description { + color: #fff; +} + +/* Release detail: solid surface only when there's no hero image. Once an image is + present, the shared --with-background-image rules (transparent + left gradient) + take over so the background scene shows through, same as community. */ +.hero--release.hero--no-image { + background: var(--color-surface-weak-accent-yellow); +} + +/* ----- Hero variants: tablet ----- */ +/* In the tablet band the community foreground is still full width but the mascot has + moved toward center, so cap the text column at 50vw to keep it clear of the art. + Banded (not <=1279) so mobile, where the foreground stacks in-flow below the text, + keeps the full-width text. */ +@media (min-width: 768px) and (max-width: 1279px) { + .hero--community .hero__content { + max-width: min(615px, 50vw); + } +} + +/* ----- Hero variants: mobile ----- */ +@media (max-width: 767px) { + /* The mobile foreground is in-flow, so the block's padding-bottom would gap it + from the edge. Zero it so the figure sits flush, matching the home hero. */ + .hero--community .hero__block { + padding-bottom: 0; + } + + /* Community foreground (mobile) uses the shared `.hero-fg` mobile rule above. */ + + .hero--release .hero__block { + padding: 80px 0 0 0; + justify-content: flex-start; + } + + .hero--release .hero__content { + margin: 0; + } +} diff --git a/static/css/v3/library-subpage.css b/static/css/v3/library-subpage.css index ec6800494..8adedfabc 100644 --- a/static/css/v3/library-subpage.css +++ b/static/css/v3/library-subpage.css @@ -22,12 +22,6 @@ /* ── Page wrapper ─────────────────────────────────────────────────────────── */ -body:has(.library-subpage) .header { - position: absolute; - z-index: 10; - left: 0; - right: 0; -} .library-subpage { display: flex; diff --git a/static/css/v3/release-detail.css b/static/css/v3/release-detail.css index 240c04d4a..dc180d9be 100644 --- a/static/css/v3/release-detail.css +++ b/static/css/v3/release-detail.css @@ -13,17 +13,8 @@ flex: 1 0 auto; } -body:has(.releases-v3) .header { - position: absolute; - z-index: 10; - left: 0; - right: 0; -} - -/* Hero background color override for release pages */ -body:has(.releases-v3) .hero-library--no-image { - background: var(--color-surface-weak-accent-yellow); -} +/* Hero styling (including the --release variant) lives in heros.css; header + positioning is centralized in header.css (body.v3 .header). */ .release-detail { display: flex; @@ -141,25 +132,6 @@ body:has(.releases-v3) .hero-library--no-image { grid-column: auto; } - body:has(.releases-v3) .hero-library--no-image .hero-library__block { - height: 520px; - padding: 80px 0 0 0; - } - - body:has(.releases-v3) .hero-library--no-image .hero-library__main { - justify-content: flex-start; - } - - body:has(.releases-v3) .hero-library--no-image .hero-library__content { - padding: 0 var(--space-medium); - width: 100%; - } - - body:has(.releases-v3) .hero-library--no-image .banner { - margin: 0 var(--space-medium); - padding: var(--space-default) var(--space-large); - } - .downloads-table-card__td--hash { max-width: 100px; } diff --git a/static/css/v3/v3-homepage.css b/static/css/v3/v3-homepage.css index dbb5b1a05..d85792d0b 100644 --- a/static/css/v3/v3-homepage.css +++ b/static/css/v3/v3-homepage.css @@ -2,25 +2,6 @@ body:has(.homepage-v3) { overflow-x: clip; } -body:has(.homepage-v3) .header { - position: absolute; - z-index: 10; - left: 0; - right: 0; -} - -.homepage-container .hero-home { - border: none; - border-radius: 0; - width: 100vw; - position: relative; - left: 50%; - right: 50%; - margin-left: -50vw; - margin-right: -50vw; - box-sizing: border-box; -} - .homepage-container { max-width: 1440px; margin: 0 auto; @@ -180,11 +161,6 @@ body:has(.homepage-v3) .header { } @media (max-width: 767px) { - .homepage-container .hero-home { - padding-left: var(--space-default); - padding-right: var(--space-default); - } - .homepage-container { max-width: 485px; padding: 0 var(--space-medium); diff --git a/templates/v3/community.html b/templates/v3/community.html index 7925c6d9f..fb02581be 100644 --- a/templates/v3/community.html +++ b/templates/v3/community.html @@ -9,8 +9,7 @@ {% block main_content_wrapper %}
{% endblock %} {% block content %} - {% large_static "img/v3/community-page/community-foreground.png" as hero_image_url %} - {% include "v3/includes/_hero_library.html" with title="A community built to help you go further" description="For newcomers and experienced engineers alike, Boost is a place to build real libraries, learn from experts and make contributions that matter." hero_image_url=hero_image_url %} + {% include "v3/includes/_hero_library.html" with title="Built to help you go further" description="Connect, collaborate, and grow with engineers from around the world." hero_image_url=hero_image_url hero_background_image_url=hero_background_image_url extra_classes="hero--community" fullbleed_fg=True %}
{% include "v3/includes/_help_card.html" with heading="What do you need help with?" items=help_options extra_classes="community-masonry__help" %} diff --git a/templates/v3/examples/_v3_example_section.html b/templates/v3/examples/_v3_example_section.html index f8f52ae11..51e9f894d 100644 --- a/templates/v3/examples/_v3_example_section.html +++ b/templates/v3/examples/_v3_example_section.html @@ -85,7 +85,7 @@

{{ section_title }}

{{ section_title }}

- {% include "v3/includes/_hero_home.html" with hero_background_image_url="" install_card_id="hero-home-1" hero_image_url_light=hero_legacy_image_url_light hero_image_url_dark=hero_legacy_image_url_dark %} + {% include "v3/includes/_hero_home.html" with hero_background_image_url=hero_background_image_url install_card_id="hero-home-1" hero_image_url_light=hero_image_url_light hero_image_url_dark=hero_image_url_dark %} {% include "v3/includes/_hero_home.html" with install_card_id="hero-home-2" hero_image_url=hero_image_url %} {% include "v3/includes/_hero_home.html" with install_card_id="hero-home-3" hero_image_url="" hero_image_url_light="" hero_image_url_dark="" %}
diff --git a/templates/v3/homepage.html b/templates/v3/homepage.html index f81b984ac..5d7710d63 100644 --- a/templates/v3/homepage.html +++ b/templates/v3/homepage.html @@ -10,8 +10,8 @@ {% endblock %} {% block content %} + {% include "v3/includes/_hero_home.html" with hero_background_image_url=hero_background_image_url install_card_id="hero-home-1" hero_image_url_light=hero_image_url_light hero_image_url_dark=hero_image_url_dark hero_image_url_mobile=hero_image_url_mobile %}
- {% include "v3/includes/_hero_home.html" with hero_background_image_url="" install_card_id="hero-home-1" hero_image_url_light=hero_legacy_image_url_light hero_image_url_dark=hero_legacy_image_url_dark %}
{% include "v3/includes/_search_card.html" with heading="What are you trying to find?" action_url="#" popular_terms=popular_terms %} diff --git a/templates/v3/includes/_hero_home.html b/templates/v3/includes/_hero_home.html index 6e3229d57..a0607c281 100644 --- a/templates/v3/includes/_hero_home.html +++ b/templates/v3/includes/_hero_home.html @@ -13,33 +13,40 @@ hero_image_url (optional) — Image URL for the right side. If omitted, image block is not shown. hero_image_url_light (optional) — Image URL for light mode. If omitted, falls back to hero_image_url. hero_image_url_dark (optional) — Image URL for dark mode. If omitted, only the light image is used. + hero_image_url_mobile (optional) — Art-directed image swapped in at <=767px (e.g. a tighter crop). If omitted, the desktop image is used at all widths. hero_background_image_url (optional) — Background image URL for the hero container. Usage: {% include "v3/includes/_hero_home.html" %} {% include "v3/includes/_hero_home.html" with headline="Welcome to Boost" primary_url="/docs/" hero_image_url=hero_image_url %} {% endcomment %} -
-
-
-

- {{ headline|default:"Built by the C++ community. Trusted by the world." }}

-
+
+
+

+ {% if headline %}{{ headline }}{% else %}Built by the
C++
community.
Trusted by
the world.{% endif %}

+
{% include "v3/includes/_button_hero.html" with label=primary_label|default:"I'm new to Boost" url=primary_url style="primary" type="button" %} {% include "v3/includes/_button_hero.html" with label=secondary_label|default:"I need a specific solution" url=secondary_url style="secondary" type="button" %}
-
+
{% include "v3/includes/_install_card.html" with title=install_card_title|default:"Install Boost and get started in your terminal." card_id=install_card_id|default:"hero-home-install" %}
{% if hero_image_url or hero_image_url_light or hero_image_url_dark %} -
+
{% with hero_light=hero_image_url_light|default:hero_image_url hero_dark=hero_image_url_dark %} - + + {% if hero_image_url_mobile %}{% endif %} + + {% if hero_dark %} - + + {% if hero_image_url_mobile %}{% endif %} + + {% endif %} {% endwith %}
diff --git a/templates/v3/includes/_hero_library.html b/templates/v3/includes/_hero_library.html index abe432575..a6269b666 100644 --- a/templates/v3/includes/_hero_library.html +++ b/templates/v3/includes/_hero_library.html @@ -13,32 +13,27 @@ source_url (optional) — Source code link URL. Omit to hide. slack_url (optional) — Discuss in Slack URL. Omit to hide. github_url (optional) — GitHub Issues URL. Omit to hide. - rss_url (optional) — RSS feed URL. Omit to hide. hero_image_url (optional) — Image URL. If omitted, image block is not shown. + hero_image_url_mobile (optional) — Art-directed image swapped in at <=767px (e.g. a tighter crop). If omitted, the desktop image is used at all widths. hero_background_image_url (optional) — Background image URL for the hero container. + extra_classes (optional) — Extra modifier classes for the hero
, e.g. "hero--community" (community) or "hero--release" (release detail). is_flagship_lib (optional) — Boolean value stating whether or not this is the hero for a flagship library. If it isn't, styles are a bit different. If none of category_tags/version_tag/added_text are passed, the tags row is - hidden. If none of doc_url/source_url/slack_url/github_url/rss_url are - passed, the links row is hidden. - - When `version_alert` is in context (along with version_alert_message and - selected_version), a v3/includes/_version_alert.html banner is rendered as - the last child of the hero section. Note: callers using `only` must forward - these three vars explicitly. + hidden. If none of doc_url/source_url/slack_url/github_url are passed, the + links row is hidden. Usage: {% include "v3/includes/_hero_library.html" with title="Boost.Asio" description="..." doc_url="/doc/libs/..." %} {% endcomment %} -
-
-
-
-
+
+
+
{% if category_tags or version_tag or added_text %} -
+
{% for tag in category_tags %} {% include "v3/includes/_category_tag.html" with tag_label=tag.label url=tag.url|default:"#" variant="neutral" size="default" %} {% endfor %} @@ -46,24 +41,23 @@ {{ version_tag }} {% endif %} {% if added_text %} -
- {{ added_text }} +
+ {{ added_text }}
{% endif %}
{% endif %} -
+
{% if title %} -

{{ title }}

+

{{ title }}

{% endif %} {% if description %} -

{{ description }}

+

{{ description }}

{% endif %}
{% if doc_url or source_url or slack_url or github_url or rss_url %} - - {% if hero_image_url or hero_image_url_light or hero_image_url_dark %} -
+ {# fullbleed_fg: community uses the shared .hero-fg frame; other library heroes keep the small-box .hero__image. #} + {% with fgwrap=fullbleed_fg|yesno:"hero-fg,hero__image" fgimg=fullbleed_fg|yesno:"hero-fg__img,hero__img" %} +
{% with hero_light=hero_image_url_light|default:hero_image_url hero_dark=hero_image_url_dark %} - + + {% if hero_image_url_mobile %}{% endif %} + + {% if hero_dark %} - + + {% if hero_image_url_mobile %}{% endif %} + + {% endif %} {% endwith %}
+ {% endwith %} {% endif %} -
- {% include "v3/includes/_version_alert.html" %}
+ {% include "v3/includes/_version_alert.html" %}
diff --git a/templates/v3/release_detail.html b/templates/v3/release_detail.html index 41a48952f..17d38eb88 100644 --- a/templates/v3/release_detail.html +++ b/templates/v3/release_detail.html @@ -20,7 +20,7 @@ {% if selected_version %} {% url 'downloads_feed_rss' as downloads_feed_rss_url %} - {% include "v3/includes/_hero_library.html" with title=hero_title doc_url=documentation_url slack_url="https://cppalliance.org/slack/" source_url=version.github_url github_url="https://github.com/boostorg/boost/issues" rss_url=downloads_feed_rss_url version_alert=version_alert version_alert_message=version_alert_message selected_version=selected_version only %} + {% include "v3/includes/_hero_library.html" with title=hero_title doc_url=documentation_url slack_url="https://cppalliance.org/slack/" source_url=version.github_url github_url="https://github.com/boostorg/boost/issues" rss_url=downloads_feed_rss_url version_alert=version_alert version_alert_message=version_alert_message selected_version=selected_version hero_image_url=hero_image_url hero_image_url_mobile=hero_image_url_mobile hero_background_image_url=hero_background_image_url extra_classes="hero--release" only %}
diff --git a/versions/views.py b/versions/views.py index 2613e76bd..641ddbbce 100755 --- a/versions/views.py +++ b/versions/views.py @@ -21,6 +21,7 @@ from waffle import flag_is_active from core.mixins import V3Mixin +from core.templatetags.custom_static import large_static from mailing_list.mixins import MailingListCardMixin from core.models import RenderedContent from libraries.constants import LATEST_RELEASE_URL_PATH_STR @@ -196,6 +197,17 @@ def get_v3_context_data(self, **kwargs): ctx = { "hero_title": f"{heading} ({obj.display_name})", + # Hero illustration: foreground + art-directed mobile crop + background + # scene. Assets land under static/static-large/img/v3/releases-page/. + "hero_image_url": large_static( + "img/v3/releases-page/release-foreground.png" + ), + "hero_image_url_mobile": large_static( + "img/v3/releases-page/release-foreground-mobile.png" + ), + "hero_background_image_url": large_static( + "img/v3/releases-page/release-background.png" + ), "is_development_branch": is_development_branch, "whats_new_heading": f"What's new in {obj.display_name}", "whats_new_approved": obj.whats_new_approved, From 8a18b376d0c540ff2730bce26f939942d7ece574 Mon Sep 17 00:00:00 2001 From: julioest Date: Wed, 24 Jun 2026 21:00:35 -0400 Subject: [PATCH 2/2] Address CodeRabbit review on hero Scopes the community background sizing to .hero--community so library and release heroes keep the base cover instead of inheriting the community art's per-breakpoint sizing. Also guards the light hero with an hero_light check so a dark-only config can't emit an empty image src. This uses {% if hero_light %} rather than the suggested chained default, because the community and release includes use 'only' and a missing variable used as a filter argument raises VariableDoesNotExist. --- static/css/v3/heros.css | 9 +++++++-- templates/v3/includes/_hero_home.html | 2 ++ templates/v3/includes/_hero_library.html | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/static/css/v3/heros.css b/static/css/v3/heros.css index b194c931a..23f7c697c 100644 --- a/static/css/v3/heros.css +++ b/static/css/v3/heros.css @@ -401,7 +401,10 @@ background-image: var(--hero-bg-image); background-repeat: no-repeat; background-position: center center; - background-size: var(--hero-bg-community-size); + /* Base stays cover; the community art's tuned per-breakpoint sizing + (--hero-bg-community-size) is applied only on .hero--community so library + and release heroes don't inherit it. */ + background-size: cover; background-color: var(--color-surface-weak-accent-yellow); align-items: center; } @@ -707,9 +710,11 @@ html.dark .hero__links .btn-icon-library:hover { --release release detail: solid surface, taller block, no foreground ═══════════════════════════════════════════════════════════════════════════ */ -/* Community: background focal point (independent of other library heroes) */ +/* Community: background focal point + tuned per-breakpoint sizing (independent of + other library heroes, which keep the base `cover`). */ .hero--community { background-position: var(--hero-bg-community-position-x) var(--hero-bg-community-position-y); + background-size: var(--hero-bg-community-size); } /* Community foreground uses the shared `.hero-fg` frame (gated by fullbleed_fg in diff --git a/templates/v3/includes/_hero_home.html b/templates/v3/includes/_hero_home.html index a0607c281..b7e664176 100644 --- a/templates/v3/includes/_hero_home.html +++ b/templates/v3/includes/_hero_home.html @@ -38,10 +38,12 @@

{% if hero_image_url or hero_image_url_light or hero_image_url_dark %}
{% with hero_light=hero_image_url_light|default:hero_image_url hero_dark=hero_image_url_dark %} + {% if hero_light %} {% if hero_image_url_mobile %}{% endif %} + {% endif %} {% if hero_dark %} {% if hero_image_url_mobile %}{% endif %} diff --git a/templates/v3/includes/_hero_library.html b/templates/v3/includes/_hero_library.html index a6269b666..5ea13a4da 100644 --- a/templates/v3/includes/_hero_library.html +++ b/templates/v3/includes/_hero_library.html @@ -81,10 +81,12 @@

{% with hero_light=hero_image_url_light|default:hero_image_url hero_dark=hero_image_url_dark %} + {% if hero_light %} {% if hero_image_url_mobile %}{% endif %} + {% endif %} {% if hero_dark %} {% if hero_image_url_mobile %}{% endif %}