Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions src/components/AnnouncementBanner/banner.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
:root {
--banner-height: 37px;
}

/* Push page content below the banner; the fixed navbar already accounts for itself */
body {
padding-top: var(--banner-height) !important;
}

.announcement-banner {
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--clone {
display: none;
}

.announcement-banner__text strong {
font-weight: 600;
}

/* Shared link Button, adapted to the dark banner */
.announcement-banner a.announcement-banner__cta.button-link {
flex-shrink: 0;
gap: 8px;
padding: 0;
color: #fff;
font-size: 14px;
line-height: 21px;
font-weight: 500;
white-space: nowrap;
}

.announcement-banner a.announcement-banner__cta.button-link::after {
bottom: -2px;
}

.announcement-banner a.announcement-banner__cta.button-link img {
filter: brightness(0) invert(1);
}

.announcement-banner a.announcement-banner__cta.button-link:focus-visible {
outline: 2px solid #fff;
outline-offset: 4px;
}

@media (max-width: 767px) {
.announcement-banner__inner {
gap: 12px;
}

/* One line that scrolls; the CTA stays fixed on the right */
.announcement-banner__marquee {
flex: 1;
min-width: 0;
overflow: hidden;
mask-image: linear-gradient(to right, transparent, #000 12px, #000 calc(100% - 12px), transparent);
}

.announcement-banner__track {
display: flex;
width: max-content;
animation: announcement-marquee 20s linear infinite;
}

.announcement-banner__marquee:hover .announcement-banner__track,
.announcement-banner__marquee:active .announcement-banner__track {
animation-play-state: paused;
}

.announcement-banner p.announcement-banner__text {
padding-right: 32px;
white-space: nowrap;
font-size: 12px;
line-height: 18px;
}

.announcement-banner__text--clone {
display: block;
}
}

@keyframes announcement-marquee {
to {
transform: translateX(-50%);
}
}

@media (prefers-reduced-motion: reduce) {
/* No scrolling: fall back to the static, wrapped message */
.announcement-banner__marquee {
mask-image: none;
}

.announcement-banner__track {
animation: none;
width: auto;
}

.announcement-banner p.announcement-banner__text {
padding-right: 0;
white-space: normal;
}

.announcement-banner__text--clone {
display: none;
}

.announcement-banner a.announcement-banner__cta.button-link::after,
.announcement-banner a.announcement-banner__cta.button-link span {
transition: none;
}
}
52 changes: 52 additions & 0 deletions src/components/AnnouncementBanner/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { useEffect, useRef } from "react";
import Button from "../Button";
import "./banner.css";

const FRONTIER_URL = "https://nextflow.com/frontier";

const message = (
<>
Nextflow.io is not changing. Our new product, <strong>Nextflow Frontier</strong>, is live, built on Nextflow by the
team who created it.
</>
);

const AnnouncementBanner = () => {
const bannerRef = useRef<HTMLDivElement>(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 (
<div ref={bannerRef} className="announcement-banner bg-brand" role="region" aria-label="Announcement">
<div className="container announcement-banner__inner">
<div className="announcement-banner__marquee">
<div className="announcement-banner__track">
<p className="announcement-banner__text">{message}</p>
{/* Second copy makes the mobile scroll loop seamless */}
<p className="announcement-banner__text announcement-banner__text--clone" aria-hidden="true">
{message}
</p>
</div>
</div>
<Button url={FRONTIER_URL} variant="link" className="announcement-banner__cta">
Learn more
</Button>
</div>
</div>
);
};

export default AnnouncementBanner;
2 changes: 2 additions & 0 deletions src/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
@@ -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);
Expand Down Expand Up @@ -143,6 +144,7 @@ const Menu = ({}) => {

return (
<div id="header" ref={headerRef} className={headerClasses} role="navigation">
<AnnouncementBanner />
<div className="container ">
<div className="navbar-header">
{isMobile && (
Expand Down
5 changes: 5 additions & 0 deletions src/components/Menu/menu.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@
.dropdown-menu > li > a img.externalLink {
opacity: 1;
}
/* Anchor the toggle to the navbar row, not the header (which also holds the banner) */
#header > .container {
position: relative;
}

.mobile-menu-toggle {
position: absolute;
right: 15px;
Expand Down
4 changes: 3 additions & 1 deletion src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,9 @@ import arrowsIcon from "../../public/img/assets/icons/arrow.svg";
Explore Seqera's AI-powered assistant to get instant answers about Nextflow, nf-core, and more — right
from the source.
</p>
<Button url="https://cloud.seqera.io/?coScientist=open" variant="link" target="_blank"> Ask Seqera AI </Button>
<Button url="https://cloud.seqera.io/?coScientist=open" variant="link" target="_blank">
Ask Seqera AI
</Button>
</div>
</div>
</div>
Expand Down
Loading