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..23f7c697c 100644 --- a/static/css/v3/heros.css +++ b/static/css/v3/heros.css @@ -1,624 +1,775 @@ /** - * 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%; + /* 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; - } + 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 + 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 + _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..b7e664176 100644 --- a/templates/v3/includes/_hero_home.html +++ b/templates/v3/includes/_hero_home.html @@ -13,33 +13,42 @@ 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_light %} + + {% if hero_image_url_mobile %}{% endif %} + + + {% 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..5ea13a4da 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_light %} + + {% if hero_image_url_mobile %}{% endif %} + + + {% 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,