feat(nx-dev): update top-level navbar

This commit is contained in:
Juri 2024-11-20 11:16:29 +01:00 committed by Juri Strumpflohner
parent 6904789b10
commit 9921496d64
22 changed files with 322 additions and 403 deletions

View File

@ -2,7 +2,7 @@
title: Improve your architecture and CI pipeline times with Nx projects title: Improve your architecture and CI pipeline times with Nx projects
slug: improve-architecture-and-ci-times-with-projects slug: improve-architecture-and-ci-times-with-projects
authors: [Philip Fulcher] authors: [Philip Fulcher]
tags: [nx, enterprise] tags: [nx, 'customer story']
cover_image: '/blog/images/2024-10-25/header.avif' cover_image: '/blog/images/2024-10-25/header.avif'
pinned: true pinned: true
--- ---

View File

@ -2,6 +2,7 @@ import type { Metadata, ResolvingMetadata } from 'next';
import { blogApi } from '../../../lib/blog.api'; import { blogApi } from '../../../lib/blog.api';
import { BlogDetails } from '@nx/nx-dev/ui-blog'; import { BlogDetails } from '@nx/nx-dev/ui-blog';
import { DefaultLayout } from '@nx/nx-dev/ui-common'; import { DefaultLayout } from '@nx/nx-dev/ui-common';
import { tryNxCloudForFree } from '../../../lib/components/headerCtaConfigs';
interface BlogPostDetailProps { interface BlogPostDetailProps {
params: { slug: string }; params: { slug: string };
@ -44,12 +45,13 @@ export async function generateStaticParams() {
export default async function BlogPostDetail({ export default async function BlogPostDetail({
params: { slug }, params: { slug },
}: BlogPostDetailProps) { }: BlogPostDetailProps) {
const ctaHeaderConfig = [tryNxCloudForFree];
const blog = await blogApi.getBlogPostBySlug(slug); const blog = await blogApi.getBlogPostBySlug(slug);
return blog ? ( return blog ? (
<> <>
{/* This empty div is necessary as app router does not automatically scroll on route changes */} {/* This empty div is necessary as app router does not automatically scroll on route changes */}
<div></div> <div></div>
<DefaultLayout> <DefaultLayout headerCTAConfig={ctaHeaderConfig}>
<BlogDetails post={blog} /> <BlogDetails post={blog} />
</DefaultLayout> </DefaultLayout>
</> </>

View File

@ -3,6 +3,10 @@ import { blogApi } from '../../lib/blog.api';
import { BlogContainer } from '@nx/nx-dev/ui-blog'; import { BlogContainer } from '@nx/nx-dev/ui-blog';
import { DefaultLayout } from '@nx/nx-dev/ui-common'; import { DefaultLayout } from '@nx/nx-dev/ui-common';
import { Suspense } from 'react'; import { Suspense } from 'react';
import {
requestFreeTrial,
tryNxCloudForFree,
} from '../../lib/components/headerCtaConfigs';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Nx Blog - Updates from the Nx & Nx Cloud team', title: 'Nx Blog - Updates from the Nx & Nx Cloud team',
@ -34,11 +38,13 @@ async function getBlogTags() {
} }
export default async function BlogIndex() { export default async function BlogIndex() {
const ctaHeaderConfig = [tryNxCloudForFree];
const blogs = await getBlogs(); const blogs = await getBlogs();
const tags = await getBlogTags(); const tags = await getBlogTags();
return ( return (
<Suspense> <Suspense>
<DefaultLayout> <DefaultLayout headerCTAConfig={ctaHeaderConfig}>
<BlogContainer blogPosts={blogs} tags={tags} /> <BlogContainer blogPosts={blogs} tags={tags} />
</DefaultLayout> </DefaultLayout>
</Suspense> </Suspense>

View File

@ -9,7 +9,11 @@ import {
Statistics, Statistics,
} from '@nx/nx-dev/ui-cloud'; } from '@nx/nx-dev/ui-cloud';
import { CallToAction, DefaultLayout } from '@nx/nx-dev/ui-common'; import {
ButtonLinkProps,
CallToAction,
DefaultLayout,
} from '@nx/nx-dev/ui-common';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
@ -37,8 +41,18 @@ export const metadata: Metadata = {
}; };
export default function NxCloudPage(): JSX.Element { export default function NxCloudPage(): JSX.Element {
const headerCTAConfig: ButtonLinkProps[] = [
{
href: '/pricing',
variant: 'primary',
size: 'small',
title: 'Get started for free',
children: 'Get started for free',
},
];
return ( return (
<DefaultLayout> <DefaultLayout headerCTAConfig={headerCTAConfig}>
<Hero /> <Hero />
<TrustedBy /> <TrustedBy />

View File

@ -1,10 +1,11 @@
import { DefaultLayout } from '@nx/nx-dev/ui-common'; import { ButtonLinkProps, DefaultLayout } from '@nx/nx-dev/ui-common';
import { import {
CallToAction, CallToAction,
GetStarted, GetStarted,
Hero, Hero,
PowerpackFeatures, PowerpackFeatures,
} from '@nx/nx-dev/ui-powerpack'; } from '@nx/nx-dev/ui-powerpack';
import { contactButton } from '../../lib/components/headerCtaConfigs';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
@ -32,8 +33,18 @@ export const metadata: Metadata = {
}; };
export default function NxPowerPackPage(): JSX.Element { export default function NxPowerPackPage(): JSX.Element {
const headerCTAConfig: ButtonLinkProps[] = [
{
href: 'https://cloud.nx.app/powerpack/purchase?licenseBusinessType=small&utm_source=nx.dev&utm_medium=referral&utm_campaign=nx-powerpackurl',
variant: 'primary',
size: 'small',
title: 'Request a free trial',
children: 'Request a free trial',
},
];
return ( return (
<DefaultLayout> <DefaultLayout headerCTAConfig={headerCTAConfig}>
<Hero /> <Hero />
<div className="mt-32 scroll-mt-32 lg:mt-56" id="features"> <div className="mt-32 scroll-mt-32 lg:mt-56" id="features">

View File

@ -12,6 +12,7 @@ import {
Testimonials, Testimonials,
TrustedBy, TrustedBy,
} from '@nx/nx-dev/ui-common'; } from '@nx/nx-dev/ui-common';
import { gotoAppButton } from '../../lib/components/headerCtaConfigs';
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Nx Cloud - Available Plans', title: 'Nx Cloud - Available Plans',
@ -38,7 +39,7 @@ export const metadata: Metadata = {
export default function PricingPage() { export default function PricingPage() {
return ( return (
<DefaultLayout> <DefaultLayout headerCTAConfig={[gotoAppButton]}>
<PlansDisplay /> <PlansDisplay />
<div className="mt-18 lg:mt-32"> <div className="mt-18 lg:mt-32">
<TrustedBy utmSource="pricingpage" utmCampaign="pricing" /> <TrustedBy utmSource="pricingpage" utmCampaign="pricing" />

View File

@ -0,0 +1,41 @@
import { ButtonLinkProps } from '@nx/nx-dev/ui-common';
import { NxCloudAnimatedIcon } from '@nx/nx-dev/ui-icons';
export const requestFreeTrial: ButtonLinkProps = {
href: '/contact/sales',
variant: 'primary',
size: 'small',
title: 'Request a free trial',
children: 'Request a free trial',
};
export const tryNxCloudForFree: ButtonLinkProps = {
href: '/pricing',
variant: 'primary',
size: 'small',
title: 'Try Nx Cloud for free',
children: 'Try Nx Cloud for free',
};
export const gotoAppButton: ButtonLinkProps = {
href: 'https://nx.app/?utm_source=nx.dev&utm_medium=header-menu',
variant: 'secondary',
size: 'small',
target: '_blank',
title: 'Log in to your Nx Cloud Account',
children: (
<>
<NxCloudAnimatedIcon className="h-4 w-4" aria-hidden="true" />
<span>Go to app</span>
</>
),
};
export const contactButton: ButtonLinkProps = {
href: '/contact',
variant: 'secondary',
size: 'small',
target: '_blank',
title: 'Contact Us',
children: <span>Contact</span>,
};

View File

@ -6,6 +6,7 @@ import {
Hero, Hero,
OssProjects, OssProjects,
} from '@nx/nx-dev/ui-customers'; } from '@nx/nx-dev/ui-customers';
import { tryNxCloudForFree } from '../lib/components/headerCtaConfigs';
export function Customers(): JSX.Element { export function Customers(): JSX.Element {
const router = useRouter(); const router = useRouter();
@ -33,7 +34,7 @@ export function Customers(): JSX.Element {
type: 'website', type: 'website',
}} }}
/> />
<DefaultLayout> <DefaultLayout headerCTAConfig={[tryNxCloudForFree]}>
<div> <div>
<Hero /> <Hero />
</div> </div>

View File

@ -13,6 +13,7 @@ import {
SolveYourCi, SolveYourCi,
} from '@nx/nx-dev/ui-enterprise'; } from '@nx/nx-dev/ui-enterprise';
import { TrialCallout } from '@nx/nx-dev/ui-pricing'; import { TrialCallout } from '@nx/nx-dev/ui-pricing';
import { requestFreeTrial } from '../lib/components/headerCtaConfigs';
export function Enterprise(): JSX.Element { export function Enterprise(): JSX.Element {
const router = useRouter(); const router = useRouter();
@ -40,7 +41,7 @@ export function Enterprise(): JSX.Element {
type: 'website', type: 'website',
}} }}
/> />
<DefaultLayout> <DefaultLayout headerCTAConfig={[requestFreeTrial]}>
<div> <div>
<Hero /> <Hero />
</div> </div>

View File

@ -8,8 +8,15 @@ import {
TeamAndCommunity, TeamAndCommunity,
WorkBetterAchieveMoreShipQuicker, WorkBetterAchieveMoreShipQuicker,
} from '@nx/nx-dev/ui-home'; } from '@nx/nx-dev/ui-home';
import {
requestFreeTrial,
gotoAppButton,
contactButton,
} from '../lib/components/headerCtaConfigs';
export default function Index(): JSX.Element { export default function Index(): JSX.Element {
const headerCTAConfig = [contactButton];
return ( return (
<> <>
<NextSeo <NextSeo
@ -34,7 +41,7 @@ export default function Index(): JSX.Element {
}} }}
/> />
<h1 className="sr-only">Build system with advanced CI capabilities.</h1> <h1 className="sr-only">Build system with advanced CI capabilities.</h1>
<DefaultLayout isHome> <DefaultLayout isHome headerCTAConfig={headerCTAConfig}>
<Hero /> <Hero />
<div className="mt-16 lg:-mt-32"> <div className="mt-16 lg:-mt-32">
<Statistics /> <Statistics />

View File

@ -7,6 +7,7 @@ import { ComponentProps, Fragment, useState } from 'react';
import { ButtonLink, SectionHeading } from '@nx/nx-dev/ui-common'; import { ButtonLink, SectionHeading } from '@nx/nx-dev/ui-common';
import { MovingBorder } from '@nx/nx-dev/ui-animations'; import { MovingBorder } from '@nx/nx-dev/ui-animations';
import Image from 'next/image'; import Image from 'next/image';
import { NxCloudAnimatedIcon } from '@nx/nx-dev/ui-icons';
export function Hero(): JSX.Element { export function Hero(): JSX.Element {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -31,7 +32,8 @@ export function Hero(): JSX.Element {
variant="primary" variant="primary"
size="default" size="default"
> >
Get started <NxCloudAnimatedIcon className="h-4 w-4" aria-hidden="true" />
<span>Go to app</span>
</ButtonLink> </ButtonLink>
<ButtonLink <ButtonLink
href="/ci/intro/ci-with-nx" href="/ci/intro/ci-with-nx"

View File

@ -25,12 +25,12 @@ export * from './lib/square-dotted-pattern';
export * from './lib/live-stream-notifier'; export * from './lib/live-stream-notifier';
export { resourceMenuItems } from './lib/headers/menu-items'; export { resourceMenuItems } from './lib/headers/menu-items';
export { solutionsMenuItems } from './lib/headers/menu-items'; export { productsMenuItems as solutionsMenuItems } from './lib/headers/menu-items';
export { eventItems } from './lib/headers/menu-items'; export { eventItems } from './lib/headers/menu-items';
export { learnItems } from './lib/headers/menu-items'; export { learnItems } from './lib/headers/menu-items';
export { companyItems } from './lib/headers/menu-items'; export { companyItems } from './lib/headers/menu-items';
export type { MenuItem } from './lib/headers/menu-items'; export type { MenuItem } from './lib/headers/menu-items';
export { solutions as plans } from './lib/headers/menu-items'; export { ossProducts as plans } from './lib/headers/menu-items';
export { featuresItems } from './lib/headers/menu-items'; export { featuresItems } from './lib/headers/menu-items';
export { DefaultMenuItem } from './lib/headers/default-menu-item'; export { DefaultMenuItem } from './lib/headers/default-menu-item';
export { MobileMenuItem } from './lib/headers/mobile-menu-item'; export { MobileMenuItem } from './lib/headers/mobile-menu-item';

View File

@ -17,6 +17,12 @@ interface ButtonProps {
children: ReactNode | ReactNode[]; children: ReactNode | ReactNode[];
} }
export type ButtonLinkProps = ButtonProps & {
className?: string;
href: string;
title: string;
} & AnchorHTMLAttributes<HTMLAnchorElement>;
const variantStyles: Record<AllowedVariants, string> = { const variantStyles: Record<AllowedVariants, string> = {
primary: primary:
'bg-blue-500 dark:bg-sky-500 text-white group-hover:bg-blue-600 dark:group-hover:bg-sky-600 group-focus:ring-2 group-focus:ring-blue-500 dark:group-focus:ring-sky-500 focus:group-ring-offset-2', 'bg-blue-500 dark:bg-sky-500 text-white group-hover:bg-blue-600 dark:group-hover:bg-sky-600 group-focus:ring-2 group-focus:ring-blue-500 dark:group-focus:ring-sky-500 focus:group-ring-offset-2',
@ -99,11 +105,7 @@ export const ButtonLink = forwardRef(function (
variant = 'primary', variant = 'primary',
title = '', title = '',
...props ...props
}: ButtonProps & { }: ButtonLinkProps,
className?: string;
href: string;
title: string;
} & AnchorHTMLAttributes<HTMLAnchorElement>,
ref: ForwardedRef<HTMLAnchorElement> ref: ForwardedRef<HTMLAnchorElement>
): JSX.Element { ): JSX.Element {
return ( return (

View File

@ -1,21 +1,23 @@
import { Footer } from './footer'; import { Footer } from './footer';
import { Header } from './headers/header'; import { Header } from './headers/header';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import cx from 'classnames'; import { ButtonLinkProps } from './button';
export function DefaultLayout({ export function DefaultLayout({
isHome = false, isHome = false,
children, children,
hideHeader = false, hideHeader = false,
hideFooter = false, hideFooter = false,
headerCTAConfig,
}: { }: {
isHome?: boolean; isHome?: boolean;
hideHeader?: boolean; hideHeader?: boolean;
hideFooter?: boolean; hideFooter?: boolean;
headerCTAConfig?: ButtonLinkProps[];
} & PropsWithChildren): JSX.Element { } & PropsWithChildren): JSX.Element {
return ( return (
<div className="w-full overflow-hidden dark:bg-slate-950"> <div className="w-full overflow-hidden dark:bg-slate-950">
{!hideHeader && <Header />} {!hideHeader && <Header ctaButtons={headerCTAConfig} />}
<div className="relative isolate"> <div className="relative isolate">
<div <div
className="absolute inset-x-0 -top-40 -z-10 h-full transform-gpu overflow-hidden blur-3xl sm:-top-80" className="absolute inset-x-0 -top-40 -z-10 h-full transform-gpu overflow-hidden blur-3xl sm:-top-80"

View File

@ -1,5 +1,7 @@
'use client'; 'use client';
import { Fragment, type JSX } from 'react'; import { Fragment, type JSX } from 'react';
import { GitHubIcon } from '@nx/nx-dev/ui-icons';
import { import {
Bars3Icon, Bars3Icon,
ChevronDownIcon, ChevronDownIcon,
@ -13,9 +15,13 @@ import { ButtonLink } from '../button';
import { Popover, Transition } from '@headlessui/react'; import { Popover, Transition } from '@headlessui/react';
import { TwoColumnsMenu } from './two-columns-menu'; import { TwoColumnsMenu } from './two-columns-menu';
import { import {
companyItems,
eventItems,
featuresItems, featuresItems,
learnItems,
ossProducts,
resourceMenuItems, resourceMenuItems,
solutionsMenuItems, productsMenuItems,
} from './menu-items'; } from './menu-items';
import { SectionsMenu } from './sections-menu'; import { SectionsMenu } from './sections-menu';
import { DiscordIcon } from '../discord-icon'; import { DiscordIcon } from '../discord-icon';
@ -224,103 +230,6 @@ export function DocumentationHeader({
className="items-justified hidden justify-center space-x-2 text-sm lg:flex" className="items-justified hidden justify-center space-x-2 text-sm lg:flex"
> >
<h2 className="sr-only">Main navigation</h2> <h2 className="sr-only">Main navigation</h2>
{/*FEATURES*/}
<Popover className="relative">
{({ open }) => (
<>
<Popover.Button
className={cx(
open ? 'text-blue-500 dark:text-sky-500' : '',
'group inline-flex items-center gap-2 px-3 py-2 font-medium leading-tight outline-0 dark:text-slate-200'
)}
>
<span
className={cx(
open ? 'text-blue-500 dark:text-sky-500' : '',
'transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
)}
>
Features
</span>
<ChevronDownIcon
aria-hidden="true"
className={cx(
open
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
: '',
'h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
)}
/>
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute z-30 mt-3 w-max max-w-3xl xl:max-w-3xl">
<SectionsMenu sections={featuresItems} />
</Popover.Panel>
</Transition>
</>
)}
</Popover>
{/*SOLUTIONS*/}
<Popover className="relative">
{({ open }) => (
<>
<Popover.Button
className={cx(
open ? 'text-blue-500 dark:text-sky-500' : '',
'group inline-flex items-center px-3 py-2 font-medium leading-tight outline-0 dark:text-slate-200'
)}
>
<span
className={cx(
open ? 'text-blue-500 dark:text-sky-500' : '',
'transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
)}
>
Solutions
</span>
<ChevronDownIcon
className={cx(
open
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
: '',
'ml-2 h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
)}
aria-hidden="true"
/>
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute z-30 mt-3 w-max max-w-2xl">
<SectionsMenu sections={solutionsMenuItems} />
</Popover.Panel>
</Transition>
</>
)}
</Popover>
<Link
href="/getting-started/intro"
title="Documentation"
className="hidden px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Docs
</Link>
<Link <Link
href="/blog" href="/blog"
title="Blog" title="Blog"
@ -329,14 +238,6 @@ export function DocumentationHeader({
> >
Blog Blog
</Link> </Link>
<Link
href="/pricing"
title="Nx Cloud"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
CI Pricing
</Link>
{/*RESOURCES*/} {/*RESOURCES*/}
<Popover className="relative"> <Popover className="relative">
{({ open }) => ( {({ open }) => (
@ -377,6 +278,40 @@ export function DocumentationHeader({
</> </>
)} )}
</Popover> </Popover>
<div className="hidden h-6 w-px bg-slate-200 md:block dark:bg-slate-700" />
<Link
href="/nx-cloud"
title="Nx Cloud"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Nx Cloud
</Link>
<Link
href="/pricing"
title="Pricing"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Pricing
</Link>
<div className="hidden h-6 w-px bg-slate-200 md:block dark:bg-slate-700" />
<Link
href="/powerpack"
title="Nx Powerpack"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Powerpack
</Link>
<Link
href="/enterprise"
title="Nx Enterprise"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Enterprise
</Link>
</nav> </nav>
</div> </div>
<div className="hidden flex-grow lg:flex">{/* SPACER */}</div> <div className="hidden flex-grow lg:flex">{/* SPACER */}</div>
@ -385,22 +320,14 @@ export function DocumentationHeader({
role="menu" role="menu"
className="items-justified hidden justify-center space-x-4 lg:flex" className="items-justified hidden justify-center space-x-4 lg:flex"
> >
<Link
className="hidden cursor-pointer px-3 py-2 text-sm font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
title="Contact Us"
href="/contact"
prefetch={false}
>
Contact
</Link>
<ButtonLink <ButtonLink
href="https://nx.app/?utm_source=nx.dev&utm_medium=header-menu" href="/nx-cloud"
title="Go to app" title="Try Nx Cloud for free"
variant="secondary" variant="primary"
size="small" size="small"
> >
<NxCloudAnimatedIcon className="h-4 w-4" aria-hidden="true" /> <NxCloudAnimatedIcon className="h-4 w-4" aria-hidden="true" />
<span>Go to app</span> <span>Try Nx Cloud for free</span>
</ButtonLink> </ButtonLink>
</nav> </nav>
</div> </div>

View File

@ -8,15 +8,16 @@ import {
import cx from 'classnames'; import cx from 'classnames';
import Link from 'next/link'; import Link from 'next/link';
import { Fragment, useEffect, useState } from 'react'; import { Fragment, useEffect, useState } from 'react';
import { ButtonLink } from '../button'; import { ButtonLink, ButtonLinkProps } from '../button';
import { import {
companyItems, companyItems,
eventItems, eventItems,
featuresItems, featuresItems,
learnItems, learnItems,
solutions, ossProducts,
resourceMenuItems, resourceMenuItems,
solutionsMenuItems, productsMenuItems,
enterpriseResourcesMenuItems,
} from './menu-items'; } from './menu-items';
import { MobileMenuItem } from './mobile-menu-item'; import { MobileMenuItem } from './mobile-menu-item';
import { SectionsMenu } from './sections-menu'; import { SectionsMenu } from './sections-menu';
@ -24,7 +25,11 @@ import { TwoColumnsMenu } from './two-columns-menu';
import { AlgoliaSearch } from '@nx/nx-dev/feature-search'; import { AlgoliaSearch } from '@nx/nx-dev/feature-search';
import { GitHubIcon, NxCloudAnimatedIcon, NxIcon } from '@nx/nx-dev/ui-icons'; import { GitHubIcon, NxCloudAnimatedIcon, NxIcon } from '@nx/nx-dev/ui-icons';
export function Header(): JSX.Element { interface HeaderProps {
ctaButtons?: ButtonLinkProps[];
}
export function Header({ ctaButtons }: HeaderProps): JSX.Element {
let [isOpen, setIsOpen] = useState(false); let [isOpen, setIsOpen] = useState(false);
// We need to close the popover if the route changes or the window is resized to prevent the popover from being stuck open. // We need to close the popover if the route changes or the window is resized to prevent the popover from being stuck open.
@ -42,6 +47,19 @@ export function Header(): JSX.Element {
}; };
}, []); }, []);
const defaultCtaButtons: ButtonLinkProps[] = [
{
href: '/nx-cloud',
variant: 'primary',
size: 'small',
target: '_blank',
title: 'Try Nx Cloud for free',
children: <span>Try Nx Cloud for free</span>,
},
];
const buttonsToRender = ctaButtons || defaultCtaButtons;
return ( return (
<div className="fixed inset-x-0 top-0 isolate z-[5] flex px-4 print:hidden"> <div className="fixed inset-x-0 top-0 isolate z-[5] flex px-4 print:hidden">
<div <div
@ -69,95 +87,6 @@ export function Header(): JSX.Element {
className="items-justified flex items-center justify-center space-x-2 py-0.5" className="items-justified flex items-center justify-center space-x-2 py-0.5"
> >
<h2 className="sr-only">Main navigation</h2> <h2 className="sr-only">Main navigation</h2>
{/*FEATURES*/}
<Popover className="relative">
{({ open }) => (
<>
<Popover.Button
className={cx(
open ? 'text-blue-500 dark:text-sky-500' : '',
'group inline-flex items-center gap-2 px-3 py-2 font-medium leading-tight outline-0 dark:text-slate-200'
)}
>
<span
className={cx(
open ? 'text-blue-500 dark:text-sky-500' : '',
'transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
)}
>
Features
</span>
<ChevronDownIcon
aria-hidden="true"
className={cx(
open
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
: '',
'h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
)}
/>
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute z-10 mt-3 w-max max-w-3xl xl:max-w-3xl">
<SectionsMenu sections={featuresItems} />
</Popover.Panel>
</Transition>
</>
)}
</Popover>
{/*SOLUTIONS*/}
<Popover className="relative">
{({ open }) => (
<>
<Popover.Button
className={cx(
open ? 'text-blue-500 dark:text-sky-500' : '',
'group inline-flex items-center px-3 py-2 font-medium leading-tight outline-0 dark:text-slate-200'
)}
>
<span
className={cx(
open ? 'text-blue-500 dark:text-sky-500' : '',
'transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
)}
>
Solutions
</span>
<ChevronDownIcon
className={cx(
open
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
: '',
'ml-2 h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
)}
aria-hidden="true"
/>
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute z-10 mt-3 w-max max-w-2xl">
<SectionsMenu sections={solutionsMenuItems} />
</Popover.Panel>
</Transition>
</>
)}
</Popover>
<Link <Link
href="/getting-started/intro" href="/getting-started/intro"
title="Documentation" title="Documentation"
@ -174,14 +103,6 @@ export function Header(): JSX.Element {
> >
Blog Blog
</Link> </Link>
<Link
href="/pricing"
title="Nx Cloud"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
CI Pricing
</Link>
{/*RESOURCES*/} {/*RESOURCES*/}
<Popover className="relative"> <Popover className="relative">
{({ open }) => ( {({ open }) => (
@ -215,14 +136,49 @@ export function Header(): JSX.Element {
leaveFrom="opacity-100 translate-y-0" leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1" leaveTo="opacity-0 translate-y-1"
> >
<Popover.Panel className="absolute left-60 z-10 mt-3 w-max max-w-2xl -translate-x-1/2 transform lg:left-20"> <Popover.Panel className="absolute left-60 z-30 mt-3 w-max max-w-2xl -translate-x-1/2 transform lg:left-20">
<SectionsMenu sections={resourceMenuItems} /> <SectionsMenu sections={resourceMenuItems} />
</Popover.Panel> </Popover.Panel>
</Transition> </Transition>
</> </>
)} )}
</Popover> </Popover>
<div className="opacity-50 hover:opacity-100"> <div className="hidden h-6 w-px bg-slate-200 md:block dark:bg-slate-700" />
<Link
href="/nx-cloud"
title="Nx Cloud"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Nx Cloud
</Link>
<Link
href="/pricing"
title="Pricing"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Pricing
</Link>
<div className="hidden h-6 w-px bg-slate-200 md:block dark:bg-slate-700" />
<Link
href="/powerpack"
title="Nx Powerpack"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Powerpack
</Link>
<Link
href="/enterprise"
title="Nx Enterprise"
className="hidden gap-2 px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Enterprise
</Link>
<div className="hidden h-6 w-px bg-slate-200 md:block dark:bg-slate-700" />
<div className="px-3 opacity-50 hover:opacity-100">
<AlgoliaSearch tiny={true} /> <AlgoliaSearch tiny={true} />
</div> </div>
</nav> </nav>
@ -230,24 +186,9 @@ export function Header(): JSX.Element {
{/*SECONDARY NAVIGATION*/} {/*SECONDARY NAVIGATION*/}
<div className="flex-shrink-0 text-sm"> <div className="flex-shrink-0 text-sm">
<nav className="flex items-center justify-center space-x-1"> <nav className="flex items-center justify-center space-x-1">
<Link {buttonsToRender.map((buttonProps, index) => (
className="hidden cursor-pointer px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500" <ButtonLink key={index} {...buttonProps} />
title="Contact Us" ))}
href="/contact"
prefetch={false}
>
Contact
</Link>
<ButtonLink
href="https://cloud.nx.app"
variant="secondary"
size="small"
target="_blank"
title="Log in to your Nx Cloud Account"
>
<NxCloudAnimatedIcon className="h-4 w-4" aria-hidden="true" />
<span>Go to app</span>
</ButtonLink>
<a <a
title="Nx is open source, check the code on GitHub" title="Nx is open source, check the code on GitHub"
href="https://github.com/nrwl/nx" href="https://github.com/nrwl/nx"
@ -359,90 +300,17 @@ export function Header(): JSX.Element {
</div> </div>
<div className="relative mt-6 flex-1 px-4 sm:px-6"> <div className="relative mt-6 flex-1 px-4 sm:px-6">
<ButtonLink <ButtonLink
href="https://cloud.nx.app" href="/nx-cloud"
variant="primary" variant="primary"
size="small" size="small"
target="_blank" target="_blank"
title="Log in to your Nx Cloud Account" title="Try Nx Cloud for free"
className="w-full" className="w-full"
> >
Go to app Try Nx Cloud for free
</ButtonLink> </ButtonLink>
<div className="mt-4 divide-y divide-slate-200 border-b border-slate-200 dark:divide-slate-800 dark:border-slate-800"> <div className="mt-4 divide-y divide-slate-200 border-b border-slate-200 dark:divide-slate-800 dark:border-slate-800">
{/*FEATURES*/}
<Disclosure as="div">
{({ open }) => (
<>
<Disclosure.Button
className={cx(
open
? 'text-blue-500 dark:text-sky-500'
: 'tex-slate-800 dark:text-slate-200',
'flex w-full items-center justify-between py-4 text-left text-base font-medium focus:outline-none'
)}
>
<span>Features</span>
<ChevronDownIcon
aria-hidden="true"
className={cx(
open
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
: 'tex-slate-800 dark:text-slate-200',
'h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
)}
/>
</Disclosure.Button>
<Disclosure.Panel
as="ul"
className="space-y-1 pb-2"
>
{Object.values(featuresItems)
.flat()
.map((item) => (
<MobileMenuItem
key={item.name}
item={item}
/>
))}
</Disclosure.Panel>
</>
)}
</Disclosure>
{/*SOLUTIONS*/}
<Disclosure as="div">
{({ open }) => (
<>
<Disclosure.Button
className={cx(
open
? 'text-blue-500 dark:text-sky-500'
: 'tex-slate-800 dark:text-slate-200',
'flex w-full items-center justify-between py-4 text-left text-base font-medium focus:outline-none'
)}
>
<span>Solutions</span>
<ChevronDownIcon
aria-hidden="true"
className={cx(
open
? 'rotate-180 transform text-blue-500 dark:text-sky-500'
: 'tex-slate-800 dark:text-slate-200',
'h-3 w-3 transition duration-150 ease-in-out group-hover:text-blue-500 dark:group-hover:text-sky-500'
)}
/>
</Disclosure.Button>
<Disclosure.Panel as="ul" className="space-y-1">
{solutions.map((item) => (
<MobileMenuItem
key={item.name}
item={item}
/>
))}
</Disclosure.Panel>
</>
)}
</Disclosure>
<Link <Link
href="/getting-started/intro" href="/getting-started/intro"
title="Documentation" title="Documentation"
@ -459,24 +327,16 @@ export function Header(): JSX.Element {
> >
Blog Blog
</Link> </Link>
<Link {/*Resources*/}
href="/pricing"
title="Nx Cloud"
className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
CI Pricing
</Link>
{/*RESOURCES*/}
<Disclosure as="div"> <Disclosure as="div">
{({ open }) => ( {({ open }) => (
<> <>
<Disclosure.Button <Disclosure.Button
className={cx( className={cx(
'flex w-full items-center justify-between py-4 text-left text-base font-medium focus:outline-none',
open open
? 'text-blue-500 dark:text-sky-500' ? 'text-blue-500 dark:text-sky-500'
: 'tex-slate-800 dark:text-slate-200' : 'tex-slate-800 dark:text-slate-200',
'flex w-full items-center justify-between py-4 text-left text-base font-medium focus:outline-none'
)} )}
> >
<span>Resources</span> <span>Resources</span>
@ -494,19 +354,9 @@ export function Header(): JSX.Element {
as="ul" as="ul"
className="space-y-1 pb-2" className="space-y-1 pb-2"
> >
{learnItems.map((item) => ( {Object.values(resourceMenuItems)
<MobileMenuItem .flat()
key={item.name} .map((item) => (
item={item}
/>
))}
{eventItems.map((item) => (
<MobileMenuItem
key={item.name}
item={item}
/>
))}
{companyItems.map((item) => (
<MobileMenuItem <MobileMenuItem
key={item.name} key={item.name}
item={item} item={item}
@ -516,6 +366,38 @@ export function Header(): JSX.Element {
</> </>
)} )}
</Disclosure> </Disclosure>
<Link
href="/nx-cloud"
title="Nx Cloud"
className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Nx Cloud
</Link>
<Link
href="/pricing"
title="Pricing"
className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Pricing
</Link>
<Link
href="/powerpack"
title="Powerpack"
className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Powerpack
</Link>
<Link
href="/enterprise"
title="Enterprise"
className="flex w-full gap-2 py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
prefetch={false}
>
Enterprise
</Link>
<Link <Link
href="/contact" href="/contact"
title="Contact" title="Contact"

View File

@ -16,6 +16,7 @@ import {
MicrophoneIcon, MicrophoneIcon,
VideoCameraIcon, VideoCameraIcon,
CheckBadgeIcon, CheckBadgeIcon,
BookOpenIcon,
} from '@heroicons/react/24/outline'; } from '@heroicons/react/24/outline';
import { FC, SVGProps } from 'react'; import { FC, SVGProps } from 'react';
import { DiscordIcon } from '../discord-icon'; import { DiscordIcon } from '../discord-icon';
@ -126,11 +127,30 @@ export const featuresItems: Record<string, MenuItem[]> = {
}, },
], ],
}; };
export const solutions: MenuItem[] = [ export const ossProducts: MenuItem[] = [
{
name: 'Nx',
description: 'Smart Monorepos - Fast CI',
href: '/getting-started/intro',
icon: null,
isNew: false,
isHighlight: false,
},
{
name: 'Nx Console',
description: 'Editor integration for VSCode, Cursor and IntelliJ IDEs',
href: '/getting-started/editor-setup',
icon: null,
isNew: false,
isHighlight: false,
},
];
export const enterpriseProducts: MenuItem[] = [
{ {
name: 'Nx Cloud', name: 'Nx Cloud',
description: description:
'End-to-end solution for smart, efficient and maintainable CI.', 'Nx Cloud is the end-to-end solution for smart, efficient and maintainable CI',
href: '/nx-cloud', href: '/nx-cloud',
icon: null, icon: null,
isNew: false, isNew: false,
@ -148,7 +168,7 @@ export const solutions: MenuItem[] = [
{ {
name: 'Nx Enterprise', name: 'Nx Enterprise',
description: description:
'The ultimate Nx & Nx Cloud toolchain, tailored to your needs.', "Accelerate your organization's journey to tighter collaboration, better developer experience, and speed…lots of speed.",
href: '/enterprise', href: '/enterprise',
icon: null, icon: null,
isNew: false, isNew: false,
@ -258,10 +278,7 @@ export const companyItems: MenuItem[] = [
isHighlight: false, isHighlight: false,
}, },
]; ];
export const solutionsMenuItems = {
'Helping you grow': solutions,
// 'Use cases': useCaseItems
};
export const resourceMenuItems = { export const resourceMenuItems = {
Learn: learnItems, Learn: learnItems,
Events: eventItems, Events: eventItems,

View File

@ -208,16 +208,16 @@ export function SidebarMobile({
general: [ general: [
{ name: 'Home', href: '/', current: false }, { name: 'Home', href: '/', current: false },
{ name: 'Blog', href: '/blog', current: false }, { name: 'Blog', href: '/blog', current: false },
{ name: 'Community', href: '/community', current: false }, { name: 'Resources', href: '/resources', current: false },
{ name: 'Launch Nx', href: '/launch-nx', current: false }, { name: 'Nx Cloud', href: '/nx-cloud', current: false },
{ {
name: 'Contact', name: 'Powerpack',
href: '/contact', href: '/powerpack',
current: false, current: false,
}, },
{ {
name: 'Go to app', name: 'Enterprise',
href: 'https://cloud.nx.app', href: '/enterprise',
current: false, current: false,
}, },
], ],

View File

@ -20,9 +20,9 @@ export function Hero(): JSX.Element {
href="/contact" href="/contact"
variant="contrast" variant="contrast"
size="default" size="default"
title="Join us" title="Contact us"
> >
Reach out Contact us
</ButtonLink> </ButtonLink>
<a <a

View File

@ -17,26 +17,25 @@ export function Hero(): JSX.Element {
Accelerate your organization's journey to tighter collaboration, Accelerate your organization's journey to tighter collaboration,
better developer experience, and speedlots of speed. better developer experience, and speedlots of speed.
</SectionHeading> </SectionHeading>
<div className="mt-10"> <div className="mt-10 items-center justify-center gap-x-6">
<ButtonLink <ButtonLink
href="/contact/engineering" href="/contact/sales"
title="Talk to the engineering team" title="Request a free trial"
variant="primary" variant="primary"
size="default" size="default"
> >
Talk to engineering Request a free trial
</ButtonLink> </ButtonLink>
<p className="mt-6 italic"> <p className="mt-6 italic">
Ready to talk terms?{' '} Got questions?{' '}
<Link <Link
href="/contact/sales" href="/contact/engineering"
title="Talk to the sales team" title="Talk to the sales team"
className="font-semibold underline" className="font-semibold underline"
prefetch={false} prefetch={false}
> >
Speak directly to sales Talk to an engineer.
</Link> </Link>
.
</p> </p>
</div> </div>
</div> </div>

View File

@ -4,14 +4,8 @@ import { ShaderGradient, ShaderGradientCanvas } from 'shadergradient';
import { BlurFade, usePrefersReducedMotion } from '@nx/nx-dev/ui-animations'; import { BlurFade, usePrefersReducedMotion } from '@nx/nx-dev/ui-animations';
import { Theme, useTheme } from '@nx/nx-dev/ui-theme'; import { Theme, useTheme } from '@nx/nx-dev/ui-theme';
import { useState } from 'react'; import { useState } from 'react';
import Link from 'next/link';
import { useIsomorphicLayoutEffect } from '@nx/nx-dev/ui-primitives'; import { useIsomorphicLayoutEffect } from '@nx/nx-dev/ui-primitives';
import { import { RustIcon, TypeScriptIcon } from '@nx/nx-dev/ui-icons';
MonorepoWorldIcon,
RustIcon,
TypeScriptIcon,
} from '@nx/nx-dev/ui-icons';
import { motion, MotionConfig } from 'framer-motion';
export function Hero(): JSX.Element { export function Hero(): JSX.Element {
return ( return (
@ -76,12 +70,12 @@ export function Hero(): JSX.Element {
Learn about Nx on CI Learn about Nx on CI
</ButtonLink> </ButtonLink>
<ButtonLink <ButtonLink
href="/contact?utm_medium=website&utm_campaign=homepage_links&utm_content=cta_hero_get_started" href="/nx-cloud"
title="Get started" title="Try Nx Cloud for free"
variant="secondary" variant="secondary"
size="default" size="default"
> >
Contact us Try Nx Cloud for free
</ButtonLink> </ButtonLink>
</div> </div>

View File

@ -18,6 +18,16 @@ export function PlansDisplay(): ReactElement {
<SectionHeading as="p" variant="subtitle" className="mt-6"> <SectionHeading as="p" variant="subtitle" className="mt-6">
Level up your CI with Nx Cloud Level up your CI with Nx Cloud
</SectionHeading> </SectionHeading>
<div className="mt-10 flex items-center justify-center gap-x-6">
<ButtonLink
href="/contact/engineering"
title="Talk to the engineering team"
variant="secondary"
size="default"
>
Got questions? Talk to our team
</ButtonLink>
</div>
</header> </header>
<div className="mt-20 flow-root"> <div className="mt-20 flow-root">
<div className="isolate -mt-16 grid max-w-full grid-cols-1 gap-6 sm:mx-auto lg:mt-0 lg:grid-cols-3 xl:-mx-4 xl:gap-8"> <div className="isolate -mt-16 grid max-w-full grid-cols-1 gap-6 sm:mx-auto lg:mt-0 lg:grid-cols-3 xl:-mx-4 xl:gap-8">
@ -57,7 +67,7 @@ export function PlansDisplay(): ReactElement {
} }
className="w-full" className="w-full"
> >
Get started Start now
</ButtonLink> </ButtonLink>
</div> </div>
<ul className="mt-4 divide-y divide-slate-200 text-sm dark:divide-slate-800"> <ul className="mt-4 divide-y divide-slate-200 text-sm dark:divide-slate-800">
@ -161,7 +171,7 @@ export function PlansDisplay(): ReactElement {
} }
className="w-full" className="w-full"
> >
Get started Free to start
</ButtonLink> </ButtonLink>
</div> </div>
<ul className="mt-4 divide-y divide-slate-200 text-sm dark:divide-slate-800"> <ul className="mt-4 divide-y divide-slate-200 text-sm dark:divide-slate-800">
@ -266,21 +276,21 @@ export function PlansDisplay(): ReactElement {
</p> </p>
<div className="my-12"> <div className="my-12">
<ButtonLink <ButtonLink
href="/enterprise" href="/contact/sales"
aria-describedby="enterprise-plan" aria-describedby="enterprise-plan"
title="Enterprise" title="Enterprise"
size="default" size="default"
variant="secondary" variant="secondary"
onClick={() => onClick={() =>
sendCustomEvent( sendCustomEvent(
'learn-enterprise-click', 'request-enterprise-trial',
'plans-table', 'plans-table',
'pricing-plans' 'pricing-plans'
) )
} }
className="w-full" className="w-full"
> >
Learn more Request a trial
</ButtonLink> </ButtonLink>
</div> </div>
<ul className="mt-4 divide-y divide-slate-200 text-sm dark:divide-slate-800"> <ul className="mt-4 divide-y divide-slate-200 text-sm dark:divide-slate-800">