From b3fc685f20a2d875dea3a0edb96359494987f82e Mon Sep 17 00:00:00 2001 From: mavi-sqr Date: Mon, 12 Oct 2026 13:57:47 +0200 Subject: [PATCH 1/6] Add Nextflow Frontier announcement banner Site-wide banner above the navbar announcing Nextflow Frontier, with a "Learn more" link button. It lives inside the fixed header and offsets the page by its measured height, so it wraps cleanly on mobile. Co-Authored-By: Claude Opus 5.5 --- src/components/AnnouncementBanner/banner.css | 81 ++++++++++++++++++++ src/components/AnnouncementBanner/index.tsx | 38 +++++++++ src/components/Menu/Menu.tsx | 2 + src/components/Menu/menu.css | 5 ++ 4 files changed, 126 insertions(+) create mode 100644 src/components/AnnouncementBanner/banner.css create mode 100644 src/components/AnnouncementBanner/index.tsx diff --git a/src/components/AnnouncementBanner/banner.css b/src/components/AnnouncementBanner/banner.css new file mode 100644 index 000000000..60de7ded9 --- /dev/null +++ b/src/components/AnnouncementBanner/banner.css @@ -0,0 +1,81 @@ +:root { + --banner-height: 48px; +} + +@media (max-width: 767px) { + :root { + --banner-height: 70px; + } +} + +/* Push page content below the banner; the fixed navbar already accounts for itself */ +body { + padding-top: var(--banner-height) !important; +} + +.announcement-banner { + background: var(--color-brand, #160f26); + color: #fff; +} + +.announcement-banner__inner { + display: flex; + align-items: center; + justify-content: center; + gap: 16px; + padding-top: 8px; + padding-bottom: 8px; +} + +.announcement-banner p.announcement-banner__text { + margin: 0; + font-size: 14px; + line-height: 21px; + color: #fff; +} + +.announcement-banner__text strong { + font-weight: 600; +} + +.announcement-banner a.announcement-banner__cta { + flex-shrink: 0; + padding: 4px 12px; + border: 1px solid var(--color-nextflow-600, #0dc09d); + border-radius: 4px; + background: var(--color-nextflow-300, #86e0ce); + color: var(--color-brand, #160f26); + font-size: 14px; + line-height: 21px; + font-weight: 500; + text-decoration: none; + white-space: nowrap; + transition: background-color 0.2s ease-in-out; +} + +.announcement-banner a.announcement-banner__cta:hover { + background: var(--color-nextflow-400, #56d3ba); +} + +.announcement-banner a.announcement-banner__cta:focus-visible { + outline: 2px solid #fff; + outline-offset: 2px; +} + +@media (max-width: 767px) { + .announcement-banner__inner { + justify-content: space-between; + gap: 12px; + } + + .announcement-banner p.announcement-banner__text { + font-size: 12px; + line-height: 18px; + } +} + +@media (prefers-reduced-motion: reduce) { + .announcement-banner a.announcement-banner__cta { + transition: none; + } +} diff --git a/src/components/AnnouncementBanner/index.tsx b/src/components/AnnouncementBanner/index.tsx new file mode 100644 index 000000000..d82502bb3 --- /dev/null +++ b/src/components/AnnouncementBanner/index.tsx @@ -0,0 +1,38 @@ +import React, { useEffect, useRef } from "react"; +import "./banner.css"; + +const FRONTIER_URL = "https://seqera.io/frontier/"; + +const AnnouncementBanner = () => { + const bannerRef = useRef(null); + + // Keep the body offset in sync with the banner's real height (it wraps on small screens) + useEffect(() => { + const banner = bannerRef.current; + if (!banner) return; + const root = document.documentElement; + const observer = new ResizeObserver(([entry]) => { + root.style.setProperty("--banner-height", `${entry.target.getBoundingClientRect().height}px`); + }); + observer.observe(banner); + return () => { + observer.disconnect(); + root.style.removeProperty("--banner-height"); + }; + }, []); + + return ( +
+
+

+ Our new product, Nextflow Frontier, is live, built on Nextflow by the team who created it. +

+ + Learn more + +
+
+ ); +}; + +export default AnnouncementBanner; diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 7f20c71be..45e7e0c2d 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState, useRef } from "react"; import "./menu.css"; import Hamburger from "../Hamburger"; +import AnnouncementBanner from "../AnnouncementBanner"; const Menu = ({}) => { const [isVisible, setIsVisible] = useState(true); @@ -143,6 +144,7 @@ const Menu = ({}) => { return (