From 49453f4f1f3b493e11108ea3a44755eb894fda4b Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 8 May 2026 11:09:01 +0200 Subject: [PATCH 01/14] update shared components for new web design --- docs/app/assets/tailwind-theme.css | 21 +- .../reflex_site_shared/components/icons.py | 31 + .../src/reflex_site_shared/views/cta_card.py | 2 +- .../src/reflex_site_shared/views/footer.py | 268 ++++---- .../views/marketing_navbar.py | 636 +++++++++++------- .../views/workflow_stage.py | 125 ++++ 6 files changed, 729 insertions(+), 354 deletions(-) create mode 100644 packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py diff --git a/docs/app/assets/tailwind-theme.css b/docs/app/assets/tailwind-theme.css index 14806139e37..ee145087d28 100644 --- a/docs/app/assets/tailwind-theme.css +++ b/docs/app/assets/tailwind-theme.css @@ -1226,6 +1226,8 @@ --animate-ellipse-3: ellipse-3 2400ms ease-out infinite; --animate-ellipse-4: ellipse-4 2400ms ease-out infinite; --animate-ellipse-reversed: ellipse-reversed 2400ms ease-out infinite; + --arrow-sweep-duration: 4s; + --animate-arrow-sweep: arrow-sweep var(--arrow-sweep-duration) linear infinite backwards; /* Radius */ --radius-ui-xxs: calc(var(--radius) - 0.25rem); --radius-ui-xs: calc(var(--radius) - 0.125rem); @@ -1237,6 +1239,7 @@ /* Width */ --layout-max-width: 81rem; --docs-layout-max-width: 69rem; + --landing-layout-max-width: 68.5rem; @keyframes accordion-down { from { @@ -1631,6 +1634,20 @@ } } + @keyframes arrow-sweep { + 0% { + transform: translateX(-100%); + } + + 25% { + transform: translateX(200%); + } + + 100% { + transform: translateX(200%); + } + } + } @@ -2184,6 +2201,6 @@ } body { - @apply isolate bg-slate-1 font-sans antialiased; + @apply isolate bg-secondary-1 font-sans antialiased; } -} +} \ No newline at end of file diff --git a/packages/reflex-site-shared/src/reflex_site_shared/components/icons.py b/packages/reflex-site-shared/src/reflex_site_shared/components/icons.py index 0c4312787f1..316c40042ee 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/components/icons.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/components/icons.py @@ -572,6 +572,28 @@ circle = """ """ + +grok = """ +""" + +claude = """""" + +openai = """ +""" + +gemini = """ +""" + +cursor = """""" + +arrow_turn = """""" + +reflex_small = """""" + +docs = """""" + +curved_line = """""" + ICONS = { # Socials "github": github, @@ -663,6 +685,15 @@ "sun_footer": sun_footer, "computer_footer": computer_footer, "circle": circle, + "grok": grok, + "claude": claude, + "openai": openai, + "gemini": gemini, + "arrow_turn": arrow_turn, + "reflex_small": reflex_small, + "docs": docs, + "cursor": cursor, + "curved_line": curved_line, } diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py b/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py index 32e5f3e3288..5a53266edaf 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py @@ -46,7 +46,7 @@ def cta_card(): class_name="flex flex-col gap-6 justify-center max-w-[24.5rem]", ), rx.image( - f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/cta.svg", + f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/cta_gray_lines.svg", class_name="w-auto h-full pointer-events-none", loading="lazy", alt="CTA Card", diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py b/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py index 834361dbf51..5ba59592572 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py @@ -1,4 +1,4 @@ -"""Footer module.""" +"""Site footer layout and sections for marketing pages.""" from datetime import datetime @@ -19,35 +19,12 @@ LINKEDIN_URL, REFLEX_ASSETS_CDN, REFLEX_BUILD_URL, - ROADMAP_URL, TWITTER_URL, ) -def ph_1() -> rx.Component: - """Ph 1. - - Returns: - The component. - """ - return rx.fragment( - rx.image( - src=f"{REFLEX_ASSETS_CDN}logos/dark/ph_1.svg", - class_name="hidden dark:block h-[36px] w-fit", - alt="1st product of the day logo", - loading="lazy", - ), - rx.image( - src=f"{REFLEX_ASSETS_CDN}logos/light/ph_1.svg", - class_name="dark:hidden block h-[36px] w-fit", - alt="1st product of the day logo", - loading="lazy", - ), - ) - - def logo() -> rx.Component: - """Logo. + """Homepage logo link for the footer. Returns: The component. @@ -69,13 +46,13 @@ def logo() -> rx.Component: def tab_item(mode: str, icon: str) -> rx.Component: - """Tab item. + """Single color-mode toggle tab (system, light, or dark). Returns: The component. """ - active_cn = " shadow-[0_-1px_0_0_rgba(0,0,0,0.08)_inset,0_0_0_1px_rgba(0,0,0,0.08)_inset,0_1px_2px_0_rgba(0,0,0,0.02),0_1px_4px_0_rgba(0,0,0,0.02)] dark:shadow-[0_1px_0_0_rgba(255,255,255,0.16)_inset] bg-white dark:bg-m-slate-10 hover:bg-m-slate-2 dark:hover:bg-m-slate-9 text-m-slate-12 dark:text-m-slate-3" - unactive_cn = " hover:text-m-slate-12 dark:hover:text-m-slate-3 text-m-slate-7 dark:text-m-slate-6" + active_cn = " shadow-[0_-1px_0_0_rgba(0,0,0,0.08)_inset,0_0_0_1px_rgba(0,0,0,0.08)_inset,0_1px_2px_0_rgba(0,0,0,0.02),0_1px_4px_0_rgba(0,0,0,0.02)] dark:shadow-[0_1px_0_0_rgba(255,255,255,0.16)_inset] bg-white-1 hover:bg-secondary-2 text-secondary-12" + unactive_cn = " hover:text-secondary-12 text-secondary-11" return rx.el.button( get_icon(icon, class_name="shrink-0"), on_click=set_color_mode(mode), # type: ignore[reportArgumentType] @@ -88,7 +65,7 @@ def tab_item(mode: str, icon: str) -> rx.Component: def dark_mode_toggle() -> rx.Component: - """Dark mode toggle. + """Footer control group for switching color mode. Returns: The component. @@ -97,12 +74,12 @@ def dark_mode_toggle() -> rx.Component: tab_item("system", "computer_footer"), tab_item("light", "sun_footer"), tab_item("dark", "moon_footer"), - class_name="flex flex-row gap-0.5 items-center p-0.5 [box-shadow:0_1px_0_0_rgba(0,_0,_0,_0.08),_0_0_0_1px_rgba(0,_0,_0,_0.08),_0_1px_2px_0_rgba(0,_0,_0,_0.02),_0_1px_4px_0_rgba(0,_0,_0,_0.02)] w-fit mt-auto bg-m-slate-1 dark:bg-m-slate-12 rounded-[0.625rem] dark:border dark:border-m-slate-9 border border-transparent lg:ml-auto mr-px", + class_name="flex flex-row gap-0.5 items-center p-0.5 [box-shadow:0_1px_0_0_rgba(0,_0,_0,_0.08),_0_0_0_1px_rgba(0,_0,_0,_0.08),_0_1px_2px_0_rgba(0,_0,_0,_0.02),_0_1px_4px_0_rgba(0,_0,_0,_0.02)] w-fit mt-auto bg-secondary-1 rounded-[0.625rem] dark:border dark:border-secondary-4 border border-transparent", ) def footer_link(text: str, href: str) -> rx.Component: - """Footer link. + """External link styled for footer link columns. Returns: The component. @@ -116,14 +93,14 @@ def footer_link(text: str, href: str) -> rx.Component: ), href=href, target="_blank", - class_name="font-[525] text-m-slate-7 hover:text-m-slate-8 dark:hover:text-m-slate-5 dark:text-m-slate-6 text-sm transition-color w-full lg:w-fit flex flex-row justify-between items-center", + class_name="font-[525] text-secondary-11 hover:text-secondary-12 text-sm transition-color w-full lg:w-fit flex flex-row justify-between items-center min-h-[24px]", ) def footer_link_flex( heading: str, links: list[rx.Component], class_name: str = "" ) -> rx.Component: - """Footer link flex. + """Column with a heading and stacked footer links. Returns: The component. @@ -131,51 +108,52 @@ def footer_link_flex( return rx.el.div( rx.el.h3( heading, - class_name="text-xs text-m-slate-12 dark:text-m-slate-3 font-semibold w-fit mb-3", + class_name="text-xs text-secondary-12 font-[525] w-fit mb-2", ), *links, class_name=ui.cn("flex flex-col gap-2", class_name), ) -def social_menu_item(icon: str, url: str, name: str) -> rx.Component: - """Social menu item. +def social_menu_item( + icon: str, url: str, name: str, class_name: str = "" +) -> rx.Component: + """Icon link to a social profile or community. Returns: The component. """ return rx.el.elements.a( - marketing_button( - get_icon(icon, class_name="shrink-0"), - variant="ghost", - size="icon-sm", - class_name="text-m-slate-7 dark:text-m-slate-6", - native_button=False, - ), + get_icon(icon, class_name="size-5 shrink-0"), href=url, custom_attrs={"aria-label": "Social link for " + name}, target="_blank", + class_name=ui.cn( + "text-secondary-11 hover:text-secondary-12 transition-colors flex items-center justify-center size-7 rounded-md", + "lg:size-auto lg:h-full lg:w-full lg:rounded-none", + class_name, + ), ) def menu_socials() -> rx.Component: - """Menu socials. + """Row of major social and community links. Returns: The component. """ - return rx.box( + return rx.el.div( social_menu_item("twitter_footer", TWITTER_URL, "Twitter"), social_menu_item("github_navbar", GITHUB_URL, "Github"), social_menu_item("discord_navbar", DISCORD_URL, "Discord"), social_menu_item("linkedin_footer", LINKEDIN_URL, "LinkedIn"), social_menu_item("forum_footer", FORUM_URL, "Forum"), - class_name="flex flex-row items-center gap-2", + class_name="flex flex-row items-center gap-2 lg:gap-0 lg:grid lg:grid-cols-5 lg:border-y lg:border-secondary-4 lg:divide-x lg:divide-secondary-4 lg:w-full lg:h-16", ) def newsletter_input() -> rx.Component: - """Newsletter input. + """Email signup form or post-subscribe confirmation. Returns: The component. @@ -188,11 +166,11 @@ def newsletter_input() -> rx.Component: rx.icon( tag="circle-check", size=16, - class_name="!text-violet-9", + class_name="!text-primary-9", ), rx.text( "Thanks for subscribing!", - class_name="text-xs text-m-slate-7 dark:text-m-slate-6 font-semibold", + class_name="text-xs font-[525] text-secondary-12", ), class_name="flex flex-row items-center gap-2", ), @@ -205,17 +183,18 @@ def newsletter_input() -> rx.Component: class_name="flex flex-col flex-wrap gap-2", ), rx.form( - rx.el.input( + ui.input( placeholder="Email", name="input_email", type="email", required=True, - class_name="relative [box-shadow:0_-1px_0_0_rgba(0,_0,_0,_0.08)_inset,_0_0_0_1px_rgba(0,_0,_0,_0.08)_inset,_0_1px_2px_0_rgba(0,_0,_0,_0.02),_0_1px_4px_0_rgba(0,_0,_0,_0.02)] rounded-lg h-8 px-2 py-1.5 w-[12rem] text-sm placeholder:text-m-slate-7 dark:placeholder:text-m-slate-6 font-[525] focus:outline-none outline-none dark:border dark:border-m-slate-9 dark:bg-m-slate-11", + size="sm", + class_name="w-[195px]", ), - marketing_button( + ui.button( "Subscribe", type="submit", - variant="outline", + variant="outline-shadow", size="sm", class_name="w-fit max-w-full", ), @@ -228,96 +207,153 @@ def newsletter_input() -> rx.Component: def newsletter() -> rx.Component: - """Newsletter. + """Newsletter call-to-action block with heading. Returns: The component. """ return rx.el.div( - rx.text( + rx.el.span( "Get updates", - class_name="text-xs text-m-slate-7 dark:text-m-slate-6 font-semibold", + class_name="text-xs font-[525] text-secondary-12", ), newsletter_input(), - class_name="flex flex-col items-start gap-4 self-stretch", + class_name="flex flex-col items-start gap-4 self-stretch mt-6", ) @rx.memo def footer_index(class_name: str = "", grid_class_name: str = "") -> rx.Component: - """Footer index. + """Full marketing footer: logo, newsletter, links, and legal. Returns: The component. """ return rx.el.footer( rx.el.div( - logo(), rx.el.div( - footer_link_flex( - "Product", - [ - footer_link("AI Builder", REFLEX_BUILD_URL), - footer_link("Framework", "/framework"), - footer_link("Cloud", "/cloud"), - ], - ), - footer_link_flex( - "Solutions", - [ - footer_link("Enterprise", "/use-cases"), - footer_link("Finance", "/use-cases/finance"), - footer_link("Healthcare", "/use-cases/healthcare"), - footer_link("Consulting", "/use-cases/consulting"), - footer_link("Government", "/use-cases/government"), - ], - ), - footer_link_flex( - "Resources", - [ - footer_link("Documentation", "/docs"), - footer_link("FAQ", "/faq"), - footer_link("Common Errors", "/errors"), - footer_link("Roadmap", ROADMAP_URL), - footer_link("Changelog", CHANGELOG_URL), - ], - ), rx.el.div( - class_name="absolute -top-24 -right-px w-px h-24 bg-gradient-to-b from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden" + rx.el.div( + rx.el.div( + class_name="absolute -right-px -top-24 h-24 w-px bg-linear-to-b from-transparent to-current text-secondary-4 max-lg:hidden" + ), + logo(), + newsletter(), + rx.el.div( + menu_socials(), + class_name="mt-6 w-full lg:-mx-8 lg:w-[calc(100%+4rem)]", + ), + rx.el.div( + dark_mode_toggle(), + rx.el.span( + f"Copyright © {datetime.now().year} Pynecone, Inc.", + class_name="text-xs font-[525] text-secondary-11", + ), + rx.el.div( + server_status(StatusState.status), class_name="-ml-2.5" + ), + class_name="mt-auto justify-start flex flex-col items-start gap-6 pt-8", + ), + class_name="flex flex-col lg:pr-8 lg:pl-8 lg:pb-8 min-w-[337px] lg:border-r border-secondary-4 shrink-0 relative pt-16", + ), + rx.el.div( + footer_link_flex( + "Product", + [ + footer_link("AI Builder", REFLEX_BUILD_URL), + footer_link( + "Agent Toolkit", + "/docs/ai/integrations/overview/", + ), + footer_link( + "Enterprise", + "/docs/enterprise/overview/", + ), + footer_link("App Management", "/hosting"), + footer_link("Pricing", "/pricing"), + ], + ), + footer_link_flex( + "Solutions", + [ + footer_link("Enterprise", "/use-cases"), + footer_link("Finance", "/use-cases/finance"), + footer_link("Healthcare", "/use-cases/healthcare"), + footer_link("Consulting", "/use-cases/consulting"), + footer_link("Government", "/use-cases/government"), + ], + ), + footer_link_flex( + "Resources", + [ + footer_link("Blog", "/blog"), + footer_link("Templates", "/templates"), + footer_link( + "Integrations", + "/docs/ai/integrations/overview/", + ), + ], + ), + footer_link_flex( + "Migration", + [ + footer_link("From No-Code", "/migration/no-code/"), + footer_link("From Low-Code", "/migration/low-code/"), + footer_link( + "From Other Frameworks", + "/migration/other-frameworks/", + ), + footer_link( + "From Other AI Tools", + "/migration/other-ai-tools/", + ), + ], + ), + footer_link_flex( + "Developers", + [ + footer_link("Documentation", "/docs"), + footer_link("Changelog", CHANGELOG_URL), + footer_link("Common Errors", "/errors"), + ], + ), + footer_link_flex( + "Company", + [ + footer_link("About", "/about"), + footer_link( + "Careers", + "https://www.ycombinator.com/companies/reflex/jobs", + ), + footer_link( + "Privacy Policy", + "https://build.reflex.dev/privacy-policy", + ), + footer_link( + "Terms of Service", + "https://build.reflex.dev/terms-of-use", + ), + ], + ), + class_name=ui.cn( + "grid grid-cols-1 lg:grid-cols-3 gap-x-12 gap-y-12 w-full relative lg:px-8 pb-8 pt-16", + grid_class_name, + ), + ), + class_name="flex lg:flex-row flex-col max-lg:gap-6 w-full", ), - class_name=ui.cn( - "grid grid-cols-1 lg:grid-cols-3 gap-12 w-full lg:pr-12 pb-8 lg:border-r border-m-slate-4 dark:border-m-slate-10 xl:ml-auto relative", - grid_class_name, + rx.el.div( + class_name="absolute -top-px -right-24 w-24 h-px bg-linear-to-l from-transparent to-current text-secondary-4 max-lg:hidden" ), - ), - rx.el.div( - newsletter(), - ph_1(), - dark_mode_toggle(), - class_name="flex flex-col gap-6 lg:pl-12 pb-8 max-lg:justify-start", - ), - class_name="flex flex-col max-lg:gap-6 lg:flex-row w-full", - ), - rx.el.div( - server_status(StatusState.status), - rx.el.div( - rx.el.span( - f"Copyright © {datetime.now().year} Pynecone, Inc.", - class_name="text-xs text-m-slate-7 dark:text-m-slate-6 font-medium", + rx.el.div( + class_name="absolute -top-px -left-24 w-24 h-px bg-linear-to-r from-transparent to-current text-secondary-4 max-lg:hidden" ), - menu_socials(), - class_name="flex flex-row items-center gap-6", - ), - rx.el.div( - class_name="absolute -top-px -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden" - ), - rx.el.div( - class_name="absolute -top-px -left-24 w-24 h-px bg-gradient-to-r from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden" + class_name="relative flex flex-col w-full lg:border-x border-secondary-4 border-t", ), - class_name="flex flex-row items-center justify-between py-6 gap-4 w-full border-t border-m-slate-4 dark:border-m-slate-10 relative", + class_name="w-full min-w-0 lg:px-2", ), class_name=ui.cn( - "flex flex-col max-w-(--docs-layout-max-width) justify-center items-center w-full mx-auto mt-24 max-lg:px-4 overflow-hidden", + "flex flex-col w-full min-w-0 max-w-(--landing-layout-max-width) items-stretch mx-auto max-lg:px-4 lg:border-x border-secondary-4 relative", class_name, ), ) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py index d50a5a62aca..71e7da10adb 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py @@ -7,65 +7,19 @@ from reflex_site_shared.backend.get_blogs import BlogPostDict, RecentBlogsState from reflex_site_shared.components.icons import get_icon from reflex_site_shared.components.marketing_button import button as marketing_button -from reflex_site_shared.components.marquee import marquee from reflex_site_shared.constants import ( CHANGELOG_URL, - CONTRIBUTING_URL, - DISCUSSIONS_URL, + DISCORD_URL, GITHUB_STARS, GITHUB_URL, - JOBS_BOARD_URL, REFLEX_ASSETS_CDN, REFLEX_BUILD_URL, ) from reflex_site_shared.views.sidebar import navbar_sidebar_button - - -def social_proof_card(image: str) -> rx.Component: - """Social proof card. - - Returns: - The component. - """ - return rx.el.div( - rx.image( - f"{REFLEX_ASSETS_CDN}companies/{rx.color_mode_cond('light', 'dark')}/{image}_small.svg", - loading="lazy", - alt=f"{image} logo", - class_name="w-auto h-fit pointer-events-none", - ), - class_name="flex justify-center items-center px-3", - ) - - -def logos_carousel() -> rx.Component: - """Logos carousel. - - Returns: - The component. - """ - logos = [ - "agricole", - "man", - "shell", - "red_hat", - "accenture", - "dell", - "microsoft", - "world", - "ford", - "unicef", - "nike", - ] - return marquee( - *[social_proof_card(logo) for logo in logos], - direction="left", - gradient_color="light-dark(var(--c-white-1), var(--c-m-slate-11))", - class_name="h-[1.625rem] w-full overflow-hidden mt-auto", - gradient_width=65, - speed=25, - pause_on_hover=False, - ) +from reflex_site_shared.views.workflow_stage import ( + workflow_stage_image, + workflow_stage_row, +) def github() -> rx.Component: @@ -152,7 +106,7 @@ def menu_trigger(title: str, content: rx.Component) -> rx.Component: def menu_content(content: rx.Component, class_name: str = "") -> rx.Component: - """Menu content. + """Styled dropdown panel wrapper for navigation menu items. Returns: The component. @@ -169,36 +123,128 @@ def menu_content(content: rx.Component, class_name: str = "") -> rx.Component: ) -def platform_item(image: str, title: str, description: str, href: str) -> rx.Component: - """Platform item. +def products_column_gutter() -> rx.Component: + """Vertical separator between product mega-menu columns. Returns: The component. """ return rx.el.div( - rx.image( - src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/{image}", - alt=title, - class_name="size-18", + role="presentation", + class_name="w-8 shrink-0 bg-secondary-1 border-secondary-4 border-x", + ) + + +def products_iterate_column_body() -> rx.Component: + """Iterate stage column: framework pitch and GitHub link. + + Returns: + The component. + """ + return rx.el.div( + rx.el.div( + rx.el.span( + "Framework", + class_name="text-base font-[525] text-secondary-12", + ), + badge("Open source"), + class_name="flex flex-row items-center gap-2.5", + ), + rx.el.p( + "The full-stack Python framework, optimized to build with AI agents—apps that can be used by humans and agents alike.", + class_name="text-secondary-11 text-sm font-[475]", + ), + rx.el.elements.a( + get_icon( + "github_navbar", + class_name="size-[18px] shrink-0 text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9", + ), + "View on GitHub", + ui.icon("ArrowUpRight03Icon", class_name="size-3 shrink-0 -ml-1.75"), + href=GITHUB_URL, + target="_blank", + class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group mt-auto", ), + class_name="flex flex-col gap-2 p-6 text-center justify-center items-center min-h-[252px] h-full", + ) + + +def products_ship_column_body() -> rx.Component: + """Ship stage column: deploy messaging and illustration. + + Returns: + The component. + """ + return rx.el.div( rx.el.div( rx.el.span( - title, - class_name="dark:text-m-slate-3 text-m-slate-12 text-sm font-[525]", + "Deploy, monitor & scale", + class_name="text-base font-[525] text-secondary-12", + ), + class_name="flex flex-row items-center gap-2.5 px-6 pt-6", + ), + rx.el.p( + "One click to enterprise-grade infrastructure. Track, version, and grow across teams.", + class_name="text-secondary-11 text-sm font-[475] px-6", + ), + rx.el.div( + rx.image( + src=f"{REFLEX_ASSETS_CDN}landing/features/{rx.color_mode_cond('light', 'dark')}/ship_navbar.svg", + alt="Deploy, monitor & scale", + loading="lazy", + class_name="h-auto w-full max-w-full object-cover", + ), + class_name="flex w-full mt-auto", + ), + class_name="flex flex-col gap-2 text-center justify-center items-center min-h-[252px]", + ) + + +def products_build_column_body() -> rx.Component: + """Build stage column: AI builder and agent toolkit teaser. + + Returns: + The component. + """ + return rx.el.div( + rx.el.div( + rx.el.span( + "AI app builder", + class_name="text-base font-[525] text-secondary-12", ), + badge("New"), + class_name="flex flex-row items-center gap-2.5 px-6 pt-6", + ), + rx.el.p( + "Describe it, Reflex builds it.", + class_name="text-secondary-11 text-sm font-[475] px-6 pb-6", + ), + rx.el.div( + get_icon("arrow_turn", class_name="shrink-0 text-secondary-10"), rx.el.span( - description, - class_name="dark:text-m-slate-6 text-m-slate-7 text-sm font-[475]", + "OR", + class_name="px-2 font-mono text-xs font-[415] uppercase text-secondary-12", ), - class_name="flex flex-col", + get_icon("arrow_turn", class_name="shrink-0 text-secondary-10 rotate-180"), + class_name="flex w-full shrink-0 items-center justify-center py-3 border-y border-secondary-4 px-6 bg-secondary-1", ), - rx.el.elements.a(class_name="absolute inset-0", href=href), - class_name="p-4 flex flex-row gap-6 relative cursor-pointer rounded-sm hover-card-shadow", + rx.el.div( + rx.el.span( + "Agent Toolkit", + class_name="text-base font-[525] text-secondary-12", + ), + class_name="flex flex-row items-center gap-2.5 px-6 pt-6", + ), + rx.el.p( + "Get started with our MCP and Skills.", + class_name="text-secondary-11 text-sm font-[475] px-6 pb-6", + ), + class_name="flex flex-col gap-2 text-center justify-center items-center min-h-[252px]", ) -def platform_content() -> rx.Component: - """Platform content. +def products_content() -> rx.Component: + """Mega-menu body for Products: build, iterate, ship columns. Returns: The component. @@ -207,67 +253,41 @@ def platform_content() -> rx.Component: rx.el.div( rx.el.div( rx.el.div( - rx.el.div( - rx.el.span( - "AI Builder", - class_name="dark:text-m-slate-3 text-m-slate-12 text-lg font-semibold mb-2", - ), - rx.el.span( - "Build production-ready web apps for your team in seconds with AI-powered code generation.", - class_name="dark:text-m-slate-6 text-m-slate-7 text-sm font-medium", - ), - class_name="p-4 flex flex-col relative hover-card-shadow rounded-md", + workflow_stage_row( + "Build", right=workflow_stage_image(sweep_index=0) ), - rx.image( - src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/ai_builder_pattern.svg", - alt="AI Builder Navbar Pattern", - class_name="pointer-events-none", - ), - rx.el.elements.a( - class_name="absolute inset-0", - href=REFLEX_BUILD_URL, - target="_blank", - ), - class_name="relative flex flex-col hover-card-shadow rounded-md", - ), - class_name="p-4 flex flex-col rounded-xl bg-white-1 dark:bg-m-slate-11 h-full shadow-card dark:shadow-card-dark dark:border-r dark:border-m-slate-9", - ), - rx.el.div( - platform_item( - "framework_pixel.svg", - "Reflex Framework", - "Iterate on full-stack apps in pure Python. No JavaScript required.", - "/open-source/", - ), - platform_item( - "cloud_pixel.svg", - "Cloud Hosting", - "Deploy your app with a single command to Reflex Cloud.", - "/hosting/", + products_build_column_body(), + class_name="flex min-w-0 flex-1 shrink-0 flex-col", ), + products_column_gutter(), rx.el.div( - rx.el.span( - "Reflex Is The Operating System ", - rx.el.br(), - " for Enterprise Apps", - class_name="dark:text-m-slate-6 text-m-slate-7 font-mono font-[415] text-[0.75rem] leading-4.5 uppercase", + workflow_stage_row( + "Iterate", + left=workflow_stage_image(size="small", sweep_index=1), + right=workflow_stage_image(size="small", sweep_index=2), ), - rx.image( - src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/squares_navbar.svg", - alt="Squares Navbar", - class_name="absolute bottom-4 right-4 pointer-events-none", + products_iterate_column_body(), + class_name="flex min-w-0 flex-1 shrink-0 flex-col", + ), + products_column_gutter(), + rx.el.div( + workflow_stage_row( + "Ship", left=workflow_stage_image(sweep_index=3) ), - class_name="relative p-4", + products_ship_column_body(), + class_name="flex min-w-0 flex-1 shrink-0 flex-col", ), - class_name="p-4 flex flex-col h-full", + class_name="flex min-h-0 w-full flex-row bg-white-1", ), - class_name="w-[46.5rem] grid grid-cols-2", + products_menu_footer(), + class_name="flex max-w-[min(100vw-2rem,1077px)] w-[1077px] flex-col overflow-hidden rounded-xl bg-secondary-1 dark:shadow-card-dark", ), + class_name="p-0", ) def solutions_item(title: str, icon: str, href: str) -> rx.Component: - """Solutions item. + """Solutions submenu row with icon and title. Returns: The component. @@ -275,16 +295,51 @@ def solutions_item(title: str, icon: str, href: str) -> rx.Component: return rx.el.elements.a( ui.icon( icon, - class_name="shrink-0 text-m-slate-7 dark:text-m-slate-6 size-4.5", + stroke_width=1.5, + class_name="shrink-0 text-secondary-11 size-5", ), title, href=href, - class_name="flex flex-row px-4 py-2 rounded-sm text-sm font-[525] text-m-slate-12 dark:text-m-slate-3 gap-3 items-center justify-start cursor-pointer hover-card-shadow", + class_name="flex flex-row px-4 py-2 rounded-sm text-sm font-[525] text-secondary-12 gap-3 items-center justify-start cursor-pointer hover-card-shadow text-nowrap", + ) + + +def solutions_row( + title: str, + icon: str, + href: str, + *, + trailing_pill: str | None = None, +) -> rx.Component: + """Full-width solutions link row with optional badge. + + Returns: + The component. + """ + link_children: list[rx.Component] = [ + ui.icon( + icon, + stroke_width=1.5, + class_name="shrink-0 text-secondary-11 size-5", + ), + rx.el.span( + title, + class_name="text-sm font-[525] text-secondary-12 text-nowrap", + ), + ] + if trailing_pill: + link_children.append( + badge(trailing_pill), + ) + return rx.el.elements.a( + *link_children, + href=href, + class_name="flex flex-row px-4 py-2 rounded-sm gap-3 items-center w-full cursor-pointer hover-card-shadow text-nowrap", ) def solutions_column(title: str, items: list[tuple[str, str, str]]) -> rx.Component: - """Solutions column. + """Grouped solutions links under a section heading. Returns: The component. @@ -293,7 +348,7 @@ def solutions_column(title: str, items: list[tuple[str, str, str]]) -> rx.Compon rx.el.div( rx.el.span( title, - class_name="font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4 border-b border-dashed dark:border-m-slate-8 border-m-slate-6 dark:text-m-slate-6 text-m-slate-7", + class_name="font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4 border-b border-dashed border-secondary-6 text-secondary-11", ), class_name="px-4 pt-4 flex flex-col", ), @@ -306,7 +361,7 @@ def solutions_column(title: str, items: list[tuple[str, str, str]]) -> rx.Compon def blog_item(post: BlogPostDict) -> rx.Component: - """Blog item. + """Compact preview card for a recent blog post. Returns: The component. @@ -316,7 +371,7 @@ def blog_item(post: BlogPostDict) -> rx.Component: rx.moment( post["date"], format="MMM DD YYYY", - class_name="text-m-slate-7 dark:text-m-slate-6 text-xs font-[415] font-mono uppercase text-nowrap", + class_name="text-secondary-11 text-xs font-[415] font-mono uppercase text-nowrap", ), rx.image( src=f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/squares_blog.svg", @@ -327,7 +382,7 @@ def blog_item(post: BlogPostDict) -> rx.Component: ), rx.el.span( post["title"], - class_name="dark:text-m-slate-3 text-m-slate-12 text-sm font-[525] group-hover:text-primary-10 dark:group-hover:text-primary-9 line-clamp-3", + class_name="text-secondary-12 text-base font-[525] group-hover:text-primary-10 line-clamp-3", ), rx.el.elements.a( href=post["url"], @@ -337,30 +392,20 @@ def blog_item(post: BlogPostDict) -> rx.Component: ) -def blog_column() -> rx.Component: - """Blog column. +def badge(text: str) -> rx.Component: + """Small pill label for nav highlights (e.g. New, Enterprise). Returns: The component. """ return rx.el.div( - rx.foreach( - RecentBlogsState.posts[:2], - blog_item, - ), - rx.el.elements.a( - "Read All in Blog", - ui.icon("ArrowRight01Icon", class_name="ml-auto"), - href="/blog", - class_name="dark:text-m-slate-3 text-m-slate-12 text-sm font-[525] h-10 flex items-center justify-start gap-2 hover:text-primary-10 dark:hover:text-primary-9 mt-auto", - ), - on_mount=RecentBlogsState.fetch_recent_blogs, - class_name="flex flex-col gap-6 p-4 h-full", + text, + class_name="text-secondary-11 text-xs font-[475] bg-secondary-1 px-1.5 h-5 rounded-md flex items-center justify-center border border-secondary-4", ) -def customers_column() -> rx.Component: - """Customers column. +def case_studies_column() -> rx.Component: + """Case study teaser and book-demo CTA for Solutions menu. Returns: The component. @@ -369,32 +414,55 @@ def customers_column() -> rx.Component: rx.el.div( rx.el.div( rx.el.span( - "Customers", - class_name="font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4 border-b border-dashed dark:border-m-slate-8 border-m-slate-6 dark:text-m-slate-6 text-m-slate-7", + "Case Studies", + class_name="font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4 border-b border-dashed border-secondary-6 text-secondary-11", ), class_name="px-4 pt-4 flex flex-col", ), rx.el.div( - rx.el.span( - "Read Stories How Teams Use Reflex", - class_name="text-m-slate-12 dark:text-m-slate-3 text-lg font-[575]", + rx.el.elements.a( + "How Autodesk saved 25% of their development time", + href="/customers/autodesk/", + class_name="text-secondary-12 text-lg font-[525] hover:text-primary-10 dark:hover:text-primary-9 mt-auto", ), - rx.el.span( - "Discover how companies build internal tools, AI apps, and production dashboards in pure Python.", - class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475]", + rx.el.div( + badge("Enterprise"), + badge("AI"), + class_name="flex flex-row gap-2", + ), + class_name="flex flex-col gap-2 px-4 pb-4", + ), + rx.el.div( + class_name="h-px w-[calc(100%+2rem)] -mx-4 shrink-0 bg-secondary-4", + ), + rx.el.div( + demo_form_dialog( + trigger=rx.el.div( + rx.el.div( + "Book a Demo", + ui.icon( + "ArrowRight01Icon", + class_name="size-5", + stroke_width=1.5, + ), + class_name="flex flex-row items-center justify-between text-sm font-[525] text-secondary-12", + ), + rx.el.span( + "Reflex in action with your team.", + class_name="text-secondary-11 text-sm font-[475]", + ), + class_name="flex flex-col px-4 py-2 rounded-sm hover-card-shadow cursor-pointer ", + ), ), - logos_carousel(), - class_name="flex flex-col gap-2 px-4 pb-4 h-full", ), - rx.el.elements.a(class_name="absolute inset-0", href="/customers/"), - class_name="flex flex-col gap-6 hover-card-shadow rounded-lg relative h-full hover:[--m-slate-11:var(--m-slate-10)] hover:shadow-card dark:hover:shadow-card-dark", + class_name="flex flex-col relative h-full justify-between", ), - class_name="p-4 block rounded-lg shadow-card dark:shadow-card-dark z-[1] bg-white-1 dark:bg-m-slate-11 dark:border-x dark:border-m-slate-9", + class_name="p-4 block z-[1] bg-secondary-1 dark:border-x dark:border-secondary-4 w-[296px]", ) def solutions_content() -> rx.Component: - """Solutions content. + """Mega-menu body for Solutions: industries, features, case studies. Returns: The component. @@ -404,111 +472,180 @@ def solutions_content() -> rx.Component: rx.el.div( rx.el.div( solutions_column( - "Who's It For", + "Industries", [ - ("Executives", "LocationUser01Icon", "/use-cases/"), - ("Developers", "SourceCodeSquareIcon", "/use-cases/"), - ("Data Teams", "DatabaseIcon", "/use-cases/"), + ("AI", "OfficeIcon", "/use-cases/"), + ("Finance", "Wallet05Icon", "/use-cases/finance/"), + ("Healthcare", "HealthIcon", "/use-cases/healthcare/"), ( - "Non Technical", - "CursorCircleSelection02Icon", - "/use-cases/", + "Tech / SaaS", + "SourceCodeCircleIcon", + "/use-cases/consulting/", ), + ("Government", "BankIcon", "/use-cases/government/"), ], ), solutions_column( - "Industries", + "Features", [ - ("Enterprise", "OfficeIcon", "/use-cases/"), - ("Finance", "Wallet05Icon", "/use-cases/finance/"), - ("Healthcare", "HealthIcon", "/use-cases/healthcare/"), + ("Security", "ShieldKeyIcon", "/docs/enterprise/overview/"), ( - "Consulting", - "DocumentValidationIcon", - "/use-cases/consulting/", + "Auth", + "LoginMethodIcon", + "/docs/authentication/authentication-overview/", + ), + ( + "Role-based access", + "UserUnlock01Icon", + "/docs/hosting/adding-members/", ), ( - "Government", - "BankIcon", - "/use-cases/government/", + "On-prem & VPC Deploy", + "ServerStack01Icon", + "/blog/on-premises-deployment/", + ), + ( + "Integrations", + "PlugSocketIcon", + "/docs/ai/integrations/overview/", ), ], ), class_name="grid grid-cols-2", ), - class_name="p-4 flex flex-col rounded-xl bg-white-1 dark:bg-m-slate-11 h-full w-[28rem] shadow-card dark:shadow-card-dark dark:border-r dark:border-m-slate-9", + class_name="p-4 flex flex-col bg-white-1 h-full w-[28rem] shadow-card dark:shadow-card-dark border-r border-secondary-4", ), - rx.el.div( - solutions_column( - "Migration", - [ - ( - "Switch from No Code", - "WebDesign01Icon", - "/migration/no-code/", - ), - ( - "Switch from Low Code", - "SourceCodeSquareIcon", - "/migration/low-code/", - ), - ( - "Switch from Other Frameworks", - "CodeIcon", - "/migration/other-frameworks/", - ), - ( - "Switch from Other AI tools", - "ArtificialIntelligence04Icon", - "/migration/other-ai-tools/", - ), - ], + case_studies_column(), + class_name="flex flex-row", + ), + ) + + +def resources_agent_column() -> rx.Component: + """Agent onboarding links (agents, MCP, skills). + + Returns: + The component. + """ + return rx.el.div( + rx.el.div( + rx.el.span( + "Agent onboarding", + class_name="font-mono font-[415] text-[0.75rem] leading-4 uppercase pb-4 border-b border-dashed border-secondary-6 text-secondary-11", + ), + class_name="px-4 pt-4 flex flex-col", + ), + rx.el.div( + solutions_row( + "Agents", + "RoboticIcon", + "/docs/ai/integrations/mcp-installation/", + trailing_pill="Overview", + ), + solutions_row( + "MCP", "McpServerIcon", "/docs/ai/integrations/mcp-overview/" + ), + solutions_row("Skills", "ToolsIcon", "/docs/ai/integrations/skills/"), + class_name="flex flex-col", + ), + class_name="flex flex-col gap-4", + ) + + +def resources_blog_column() -> rx.Component: + """Recent posts list with link to the blog index. + + Returns: + The component. + """ + return rx.el.div( + rx.foreach( + RecentBlogsState.posts[:2], + blog_item, + ), + rx.el.elements.a( + "Read All in Blog", + ui.icon("ArrowRight01Icon", class_name="size-4 shrink-0"), + href="/blog", + class_name="text-secondary-12 text-sm font-[525] flex items-center gap-1.5 hover:text-primary-10 dark:hover:text-primary-9 mt-auto pt-3", + ), + on_mount=RecentBlogsState.fetch_recent_blogs, + class_name="flex flex-col gap-4 p-8 h-full justify-between border-l border-secondary-4", + ) + + +def products_menu_footer() -> rx.Component: + """Footer links inside the Products mega-menu. + + Returns: + The component. + """ + return rx.el.div( + rx.el.div( + rx.el.elements.a( + get_icon( + "docs", + class_name="size-[18px] shrink-0 text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9", ), - class_name="p-4 flex flex-col h-full", + "View All Docs", + ui.icon("ArrowUpRight03Icon", class_name="size-3 shrink-0 -ml-1.75"), + href="/docs/ai/overview/best-practices/", + target="_blank", + class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group", ), - class_name="flex flex-row", + rx.el.elements.a( + get_icon( + "reflex_small", + class_name="size-[18px] shrink-0 text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9", + ), + "Get started for free", + ui.icon("ArrowUpRight03Icon", class_name="size-3 shrink-0 -ml-1.75"), + href=REFLEX_BUILD_URL, + target="_blank", + class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group", + ), + class_name="flex flex-row items-center gap-8 px-6 py-3", ), + class_name="flex flex-col w-full shrink-0 bg-white-1 border-t border-secondary-4", ) -def resources_content() -> rx.Component: - """Resources content. +def resources_menu_footer() -> rx.Component: + """Community links footer inside the Resources mega-menu. Returns: The component. """ - return menu_content( + return rx.el.div( rx.el.div( - rx.el.div( - solutions_column( - "Developers", - [ - ("Templates", "Layout02Icon", "/templates/"), - ( - "Integrations", - "PlugSocketIcon", - "/docs/ai-builder/integrations/overview/", - ), - ("Changelog", "Clock02Icon", CHANGELOG_URL), - ("Contributing", "GitCommitIcon", CONTRIBUTING_URL), - ("Discussion", "BubbleChatIcon", DISCUSSIONS_URL), - ("FAQ", "HelpSquareIcon", "/faq/"), - ], + rx.el.elements.a( + get_icon( + "github_navbar", + class_name="size-[18px] shrink-0 text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9", ), - class_name="p-4 flex flex-col rounded-xl bg-m-slate-1 dark:bg-m-slate-12 h-full", + "GitHub", + href=GITHUB_URL, + target="_blank", + class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group", ), - customers_column(), - rx.el.div( - blog_column(), - class_name="p-4 flex flex-col h-full bg-m-slate-1 dark:bg-m-slate-12", + rx.el.elements.a( + get_icon( + "discord_navbar", + class_name="size-[18px] shrink-0 text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9", + ), + "Discord", + href=DISCORD_URL, + target="_blank", + class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group", ), - class_name="w-[52.5rem] grid grid-cols-3", + class_name="flex flex-row items-center gap-8 px-6 py-3", ), + class_name="flex flex-col w-full shrink-0 bg-white-1 border-t border-secondary-4", ) -def about_content() -> rx.Component: - """About content. +def resources_content() -> rx.Component: + """Mega-menu body for Resources: learn, agents, blog. Returns: The component. @@ -516,26 +653,56 @@ def about_content() -> rx.Component: return menu_content( rx.el.div( rx.el.div( - solutions_item("Company", "Profile02Icon", "/about/"), - solutions_item("Careers", "WorkIcon", JOBS_BOARD_URL), - class_name="p-4 flex flex-col rounded-xl bg-white-1 h-full dark:shadow-none dark:border dark:border-m-slate-9 dark:bg-m-slate-11 shadow-card", + rx.el.div( + rx.el.div( + rx.el.div( + solutions_column( + "Learn", + [ + ("Documentation", "File02Icon", "/docs"), + ("Templates", "Layout02Icon", "/templates"), + ("Changelog", "Clock02Icon", CHANGELOG_URL), + ], + ), + resources_agent_column(), + class_name="grid grid-cols-2 gap-8 min-w-0 p-5 bg-white-1 flex-1", + ), + resources_menu_footer(), + class_name="flex flex-col min-w-0 h-full", + ), + resources_blog_column(), + class_name="grid grid-cols-1 min-[480px]:grid-cols-[minmax(0,1fr)_min(17.5rem,40%)] h-full", + ), + class_name="flex flex-col w-[728px] max-w-full overflow-hidden rounded-xl bg-secondary-1 dark:shadow-card-dark", ), - class_name="w-[12.5rem]", + class_name="p-0", ), ) def navigation_menu() -> rx.Component: - """Navigation menu. + """Desktop navigation: mega-menus, CTAs, and GitHub. Returns: The component. """ return ui.navigation_menu.root( ui.navigation_menu.list( - menu_trigger("Platform", platform_content()), - menu_trigger("Solutions", solutions_content()), + menu_trigger("Products", products_content()), menu_trigger("Resources", resources_content()), + menu_trigger("Solutions", solutions_content()), + ui.navigation_menu.item( + rx.el.elements.a( + marketing_button( + "Enterprise", + size="sm", + variant="ghost", + ), + href="/docs/enterprise/overview/", + ), + class_name="xl:flex hidden px-1", + custom_attrs={"role": "menuitem"}, + ), ui.navigation_menu.item( rx.el.elements.a( marketing_button( @@ -561,7 +728,6 @@ def navigation_menu() -> rx.Component: class_name="xl:flex hidden px-1", custom_attrs={"role": "menuitem"}, ), - menu_trigger("About", about_content()), class_name="flex flex-row items-center m-0 h-full list-none", custom_attrs={"role": "menubar"}, ), @@ -615,13 +781,13 @@ def navigation_menu() -> rx.Component: class_name="relative h-full w-full overflow-hidden rounded-[inherit]", ), unstyled=True, - class_name="relative h-[var(--popup-height)] w-[var(--popup-width)] origin-[var(--transform-origin)] rounded-xl bg-m-slate-1 dark:bg-m-slate-12 navbar-shadow transition-[opacity,transform,width,height,scale,translate] duration-150 ease-[cubic-bezier(0.22,1,0.36,1)] data-[ending-style]:ease-[ease] data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[starting-style]:scale-90 data-[starting-style]:opacity-0", + class_name="relative h-[var(--popup-height)] w-[var(--popup-width)] origin-[var(--transform-origin)] rounded-xl bg-secondary-1 navbar-shadow transition-[opacity,transform,width,height,scale,translate] duration-150 ease-[cubic-bezier(0.22,1,0.36,1)] data-[ending-style]:ease-[ease] data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[ending-style]:duration-150 data-[starting-style]:scale-90 data-[starting-style]:opacity-0", ), unstyled=True, class_name="safari-nav-positioner box-border h-[var(--positioner-height)] w-[var(--positioner-width)] max-w-[var(--available-width)] transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-[instant]:transition-none", side_offset=30, align="start", - align_offset=-20, + align_offset=-104, position_method="fixed", ), ), @@ -632,7 +798,7 @@ def navigation_menu() -> rx.Component: @rx.memo def marketing_navbar() -> rx.Component: - """Marketing navbar. + """Fixed header: hosting banner plus logo and full navigation. Returns: The component. diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py b/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py new file mode 100644 index 00000000000..29a53912c51 --- /dev/null +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py @@ -0,0 +1,125 @@ +"""Workflow stage visuals (animated arrows) used in marketing mega-menus.""" + +from typing import Literal + +import reflex_components_internal as ui + +import reflex as rx + +ArrowSize = Literal["large", "small"] + +_ARROW_VARIANTS: dict[ArrowSize, tuple[int, int]] = { + "large": (26, 136), + "small": (24, 126), +} + +# Total animated groups; delay per group = duration / _ARROW_SWEEP_GROUPS so +# the four sweeps chain end-to-end across one animation cycle. +_ARROW_SWEEP_GROUPS = 4 + + +def _arrow_paths(count: int, quote: str = '"') -> str: + return "".join( + f"" + for i in range(count) + ) + + +def _arrow_row_svg_html(count: int, width: int) -> str: + return ( + f'{_arrow_paths(count)}' + ) + + +def _arrow_mask_data_url(count: int, width: int) -> str: + return ( + f'url("data:image/svg+xml;utf8,' + f"{_arrow_paths(count, quote="%22")}")' + ) + + +def workflow_stage_image( + wrapper_class_name: str = "", + size: ArrowSize = "large", + sweep_index: int = 0, +) -> rx.Component: + """Animated arrow strip with a phased sweep highlight. + + Returns: + The component. + """ + count, width = _ARROW_VARIANTS[size] + mask_url = _arrow_mask_data_url(count, width) + + return rx.el.div( + rx.html( + _arrow_row_svg_html(count, width), + class_name="block h-full w-auto text-secondary-7", + ), + rx.el.div( + rx.el.div( + class_name="absolute -top-[7px] left-0 h-[24px] w-1/2 bg-primary-9 blur-[8px] animate-arrow-sweep", + style={ + "animationDelay": ( + f"calc(var(--arrow-sweep-duration) / {_ARROW_SWEEP_GROUPS} " + f"* {sweep_index})" + ), + }, + ), + class_name=ui.cn( + "absolute inset-0 pointer-events-none overflow-hidden", + "[mask-repeat:no-repeat] [-webkit-mask-repeat:no-repeat]", + "[mask-size:100%_100%] [-webkit-mask-size:100%_100%]", + "[mask-position:left_center] [-webkit-mask-position:left_center]", + ), + style={ + "maskImage": mask_url, + "WebkitMaskImage": mask_url, + }, + ), + class_name=ui.cn( + "relative flex h-[10px] w-auto shrink-0 items-center justify-start overflow-hidden max-lg:hidden", + wrapper_class_name, + ), + ) + + +def workflow_stage_row( + title: str, + *, + left: rx.Component | None = None, + right: rx.Component | None = None, +) -> rx.Component: + """Three-column row: optional left graphic, centered title, optional right graphic. + + Returns: + The component. + """ + left_cell = ( + rx.el.div( + left, + class_name="pointer-events-none flex min-w-0 items-center justify-self-start px-2", + ) + if left is not None + else rx.el.div(class_name="min-w-0 justify-self-start") + ) + right_cell = ( + rx.el.div( + right, + class_name="pointer-events-none flex min-w-0 items-center justify-self-end px-2", + ) + if right is not None + else rx.el.div(class_name="min-w-0 justify-self-end") + ) + return rx.el.div( + left_cell, + rx.el.span(title, class_name="shrink-0 justify-self-center text-center"), + right_cell, + class_name="grid h-12 w-full shrink-0 grid-cols-[1fr_auto_1fr] items-center bg-secondary-1 font-mono text-xs font-[415] uppercase text-secondary-12 lg:border-b border-secondary-4", + ) From a70c58a4d44735966d485498541644ea74de5343 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 8 May 2026 11:16:16 +0200 Subject: [PATCH 02/14] update learn more button --- .../src/reflex_site_shared/views/hosting_banner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py b/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py index 7f00ba51ad1..43f2db9147f 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py @@ -121,10 +121,10 @@ def hosting_banner() -> rx.Component: ui.button( "Learn more", ui.icon("ArrowRight01Icon"), - variant="ghost", + variant="ghost-highlight", size="xs", aria_label="Learn more", - class_name="text-m-slate-3 dark:hover:text-m-slate-5 max-lg:hidden", + class_name="max-lg:hidden", ), class_name="flex flex-row items-center md:gap-4 gap-2", ), From 0273edd9cf9b68775c1ee3eb6f8be37b8f9d5b6f Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 8 May 2026 11:19:23 +0200 Subject: [PATCH 03/14] another --- .../src/reflex_site_shared/views/hosting_banner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py b/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py index 43f2db9147f..97a5806c28e 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/hosting_banner.py @@ -124,7 +124,7 @@ def hosting_banner() -> rx.Component: variant="ghost-highlight", size="xs", aria_label="Learn more", - class_name="max-lg:hidden", + class_name="max-lg:hidden text-white hover:text-primary-10", ), class_name="flex flex-row items-center md:gap-4 gap-2", ), From 72cde406e9899090315d6b8a4d006bb8c8f76526 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 8 May 2026 12:23:23 +0200 Subject: [PATCH 04/14] update marketing navbar --- .../src/reflex_site_shared/views/cta_card.py | 2 +- .../src/reflex_site_shared/views/footer.py | 4 - .../views/marketing_navbar.py | 93 +++++++++++-------- 3 files changed, 57 insertions(+), 42 deletions(-) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py b/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py index 5a53266edaf..eed8cd456ab 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py @@ -18,7 +18,7 @@ def cta_card(): return rx.el.div( rx.el.div( rx.el.span( - "The Unified Platform to Build and Scale Enterprise Apps", + "The Platform to Build and Scale Enterprise Apps", class_name="text-slate-12 lg:text-3xl text-2xl font-[575]", ), rx.el.span( diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py b/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py index 5ba59592572..24b1e6e54fc 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py @@ -213,10 +213,6 @@ def newsletter() -> rx.Component: The component. """ return rx.el.div( - rx.el.span( - "Get updates", - class_name="text-xs font-[525] text-secondary-12", - ), newsletter_input(), class_name="flex flex-col items-start gap-4 self-stretch mt-6", ) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py index 71e7da10adb..837e0bf6e33 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py @@ -142,17 +142,21 @@ def products_iterate_column_body() -> rx.Component: The component. """ return rx.el.div( - rx.el.div( - rx.el.span( - "Framework", - class_name="text-base font-[525] text-secondary-12", + rx.el.elements.a( + rx.el.div( + rx.el.span( + "Framework", + class_name="text-base font-[525] text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9 transition-colors", + ), + badge("Open source"), + class_name="flex flex-row items-center gap-2.5", ), - badge("Open source"), - class_name="flex flex-row items-center gap-2.5", - ), - rx.el.p( - "The full-stack Python framework, optimized to build with AI agents—apps that can be used by humans and agents alike.", - class_name="text-secondary-11 text-sm font-[475]", + rx.el.p( + "The full-stack Python framework, optimized to build with AI agents—apps that can be used by humans and agents alike.", + class_name="text-secondary-11 text-sm font-[475]", + ), + href="/open-source/", + class_name="group flex flex-col gap-2 items-center text-center", ), rx.el.elements.a( get_icon( @@ -175,11 +179,11 @@ def products_ship_column_body() -> rx.Component: Returns: The component. """ - return rx.el.div( + return rx.el.elements.a( rx.el.div( rx.el.span( "Deploy, monitor & scale", - class_name="text-base font-[525] text-secondary-12", + class_name="text-base font-[525] text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9 transition-colors", ), class_name="flex flex-row items-center gap-2.5 px-6 pt-6", ), @@ -189,14 +193,15 @@ def products_ship_column_body() -> rx.Component: ), rx.el.div( rx.image( - src=f"{REFLEX_ASSETS_CDN}landing/features/{rx.color_mode_cond('light', 'dark')}/ship_navbar.svg", + src=f"{REFLEX_ASSETS_CDN}landing/features/{rx.color_mode_cond('light', 'dark')}/ship_navbar_2.svg", alt="Deploy, monitor & scale", loading="lazy", class_name="h-auto w-full max-w-full object-cover", ), class_name="flex w-full mt-auto", ), - class_name="flex flex-col gap-2 text-center justify-center items-center min-h-[252px]", + href="/hosting/", + class_name="group flex flex-col gap-2 text-center justify-center items-center min-h-[252px]", ) @@ -207,37 +212,51 @@ def products_build_column_body() -> rx.Component: The component. """ return rx.el.div( - rx.el.div( - rx.el.span( - "AI app builder", - class_name="text-base font-[525] text-secondary-12", + rx.el.elements.a( + rx.el.div( + rx.el.span( + "AI app builder", + class_name="text-base font-[525] text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9 transition-colors", + ), + badge("New"), + class_name="flex flex-row items-center gap-2.5 px-6 pt-6", ), - badge("New"), - class_name="flex flex-row items-center gap-2.5 px-6 pt-6", - ), - rx.el.p( - "Describe it, Reflex builds it.", - class_name="text-secondary-11 text-sm font-[475] px-6 pb-6", + rx.el.p( + "Describe it, Reflex builds it.", + class_name="text-secondary-11 text-sm font-[475] px-6 pb-6", + ), + href=REFLEX_BUILD_URL, + target="_blank", + class_name="group flex flex-col gap-2 items-center text-center", ), rx.el.div( - get_icon("arrow_turn", class_name="shrink-0 text-secondary-10"), + get_icon( + "arrow_turn", class_name="shrink-0 text-secondary-10 -translate-y-0.75" + ), rx.el.span( "OR", class_name="px-2 font-mono text-xs font-[415] uppercase text-secondary-12", ), - get_icon("arrow_turn", class_name="shrink-0 text-secondary-10 rotate-180"), + get_icon( + "arrow_turn", + class_name="shrink-0 text-secondary-10 rotate-180 translate-y-0.75", + ), class_name="flex w-full shrink-0 items-center justify-center py-3 border-y border-secondary-4 px-6 bg-secondary-1", ), - rx.el.div( - rx.el.span( - "Agent Toolkit", - class_name="text-base font-[525] text-secondary-12", + rx.el.elements.a( + rx.el.div( + rx.el.span( + "Agent Toolkit", + class_name="text-base font-[525] text-secondary-12 group-hover:text-primary-10 dark:group-hover:text-primary-9 transition-colors", + ), + class_name="flex flex-row items-center gap-2.5 px-6 pt-6", ), - class_name="flex flex-row items-center gap-2.5 px-6 pt-6", - ), - rx.el.p( - "Get started with our MCP and Skills.", - class_name="text-secondary-11 text-sm font-[475] px-6 pb-6", + rx.el.p( + "Get started with our MCP and Skills.", + class_name="text-secondary-11 text-sm font-[475] px-6 pb-6", + ), + href="/docs/ai/integrations/ai-onboarding/", + class_name="group flex flex-col gap-2 items-center text-center", ), class_name="flex flex-col gap-2 text-center justify-center items-center min-h-[252px]", ) @@ -280,7 +299,7 @@ def products_content() -> rx.Component: class_name="flex min-h-0 w-full flex-row bg-white-1", ), products_menu_footer(), - class_name="flex max-w-[min(100vw-2rem,1077px)] w-[1077px] flex-col overflow-hidden rounded-xl bg-secondary-1 dark:shadow-card-dark", + class_name="flex max-w-[min(100vw-2rem,1080px)] w-[1080px] flex-col overflow-hidden rounded-xl bg-secondary-1 dark:shadow-card-dark", ), class_name="p-0", ) @@ -787,7 +806,7 @@ def navigation_menu() -> rx.Component: class_name="safari-nav-positioner box-border h-[var(--positioner-height)] w-[var(--positioner-width)] max-w-[var(--available-width)] transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-[instant]:transition-none", side_offset=30, align="start", - align_offset=-104, + align_offset=-105, position_method="fixed", ), ), From 74caa823ab38bdf3b49845ffaf5d6400a963d359 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 8 May 2026 13:28:35 +0200 Subject: [PATCH 05/14] update link --- .../src/reflex_site_shared/views/marketing_navbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py index 837e0bf6e33..ef069d276a1 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py @@ -608,7 +608,7 @@ def products_menu_footer() -> rx.Component: ), "View All Docs", ui.icon("ArrowUpRight03Icon", class_name="size-3 shrink-0 -ml-1.75"), - href="/docs/ai/overview/best-practices/", + href="/docs/", target="_blank", class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group", ), From 5c541e8a5ca99e3a23d229f920f3207454fb5ad0 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 8 May 2026 13:58:52 +0200 Subject: [PATCH 06/14] update footer --- .../reflex_docs/templates/docpage/docpage.py | 35 ++++++++++++++++++- .../views/workflow_stage.py | 31 +++++++++------- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/docs/app/reflex_docs/templates/docpage/docpage.py b/docs/app/reflex_docs/templates/docpage/docpage.py index 96dfe4b5d6b..46880da6d8f 100644 --- a/docs/app/reflex_docs/templates/docpage/docpage.py +++ b/docs/app/reflex_docs/templates/docpage/docpage.py @@ -55,6 +55,40 @@ def footer_link_flex(heading: str, links): ) +def social_menu_item(icon: str, url: str, name: str) -> rx.Component: + return rx.el.elements.a( + marketing_button( + get_icon(icon, class_name="shrink-0"), + variant="ghost", + size="icon-sm", + class_name="text-m-slate-7 dark:text-m-slate-6", + native_button=False, + ), + href=url, + custom_attrs={"aria-label": "Social link for " + name}, + target="_blank", + ) + + +def menu_socials() -> rx.Component: + from reflex_site_shared.constants import ( + DISCORD_URL, + FORUM_URL, + GITHUB_URL, + LINKEDIN_URL, + TWITTER_URL, + ) + + return rx.box( + social_menu_item("twitter_footer", TWITTER_URL, "Twitter"), + social_menu_item("github_navbar", GITHUB_URL, "Github"), + social_menu_item("discord_navbar", DISCORD_URL, "Discord"), + social_menu_item("linkedin_footer", LINKEDIN_URL, "LinkedIn"), + social_menu_item("forum_footer", FORUM_URL, "Forum"), + class_name="flex flex-row items-center gap-2", + ) + + def thumb_card(score: int, icon: str, label: str) -> rx.Component: return rx.el.button( ui.icon( @@ -238,7 +272,6 @@ def link_pill(text: str, href: str) -> rx.Component: @rx.memo def docpage_footer(path: str): from reflex_site_shared.constants import FORUM_URL, ROADMAP_URL - from reflex_site_shared.views.footer import menu_socials return rx.el.footer( rx.box( diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py b/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py index 29a53912c51..b343e289968 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py @@ -1,6 +1,7 @@ """Workflow stage visuals (animated arrows) used in marketing mega-menus.""" from typing import Literal +from urllib.parse import quote import reflex_components_internal as ui @@ -18,30 +19,33 @@ _ARROW_SWEEP_GROUPS = 4 -def _arrow_paths(count: int, quote: str = '"') -> str: +def _arrow_paths(count: int) -> str: return "".join( - f"" + f'' for i in range(count) ) -def _arrow_row_svg_html(count: int, width: int) -> str: +def _arrow_row_svg(count: int, width: int, *, inline_style: bool = False) -> str: + style_attr = ' style="display:block;height:100%;width:auto"' if inline_style else "" return ( f'{_arrow_paths(count)}' + f'fill="none" xmlns="http://www.w3.org/2000/svg"{style_attr}>' + f"{_arrow_paths(count)}" ) +def _arrow_row_svg_html(count: int, width: int) -> str: + return _arrow_row_svg(count, width, inline_style=True) + + def _arrow_mask_data_url(count: int, width: int) -> str: - return ( - f'url("data:image/svg+xml;utf8,' - f"{_arrow_paths(count, quote="%22")}")' - ) + # Percent-encode the SVG body so Firefox/Safari accept the data URL + # in mask-image; raw `<`, `>`, and spaces are not reliably parsed. + encoded = quote(_arrow_row_svg(count, width), safe="") + return f'url("data:image/svg+xml,{encoded}")' def workflow_stage_image( @@ -87,6 +91,7 @@ def workflow_stage_image( "relative flex h-[10px] w-auto shrink-0 items-center justify-start overflow-hidden max-lg:hidden", wrapper_class_name, ), + custom_attrs={"aria-hidden": "true"}, ) From 366ef772c93e3f18d4c1c5673ad837b113ad74e2 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 8 May 2026 14:35:44 +0200 Subject: [PATCH 07/14] inline that --- .../src/reflex_site_shared/views/workflow_stage.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py b/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py index b343e289968..d5337f06de5 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/workflow_stage.py @@ -37,10 +37,6 @@ def _arrow_row_svg(count: int, width: int, *, inline_style: bool = False) -> str ) -def _arrow_row_svg_html(count: int, width: int) -> str: - return _arrow_row_svg(count, width, inline_style=True) - - def _arrow_mask_data_url(count: int, width: int) -> str: # Percent-encode the SVG body so Firefox/Safari accept the data URL # in mask-image; raw `<`, `>`, and spaces are not reliably parsed. @@ -63,7 +59,7 @@ def workflow_stage_image( return rx.el.div( rx.html( - _arrow_row_svg_html(count, width), + _arrow_row_svg(count, width, inline_style=True), class_name="block h-full w-auto text-secondary-7", ), rx.el.div( From ce702f69569605cd3df96001b7e4093e9d16f597 Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 11 May 2026 12:33:45 +0200 Subject: [PATCH 08/14] dont open internal links in new tab --- .../reflex-site-shared/src/reflex_site_shared/views/footer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py b/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py index 24b1e6e54fc..2c8b4c7905a 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/footer.py @@ -79,7 +79,7 @@ def dark_mode_toggle() -> rx.Component: def footer_link(text: str, href: str) -> rx.Component: - """External link styled for footer link columns. + """Link styled for footer link columns. Returns: The component. @@ -92,7 +92,7 @@ def footer_link(text: str, href: str) -> rx.Component: class_name="shrink-0 lg:hidden flex", ), href=href, - target="_blank", + target="_blank" if not href.startswith("/") else "", class_name="font-[525] text-secondary-11 hover:text-secondary-12 text-sm transition-color w-full lg:w-fit flex flex-row justify-between items-center min-h-[24px]", ) From 467dfeae3016bfe0710002afc4195218a9266149 Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 11 May 2026 13:54:06 +0200 Subject: [PATCH 09/14] updates --- docs/app/assets/tailwind-theme.css | 2 +- .../src/reflex_site_shared/views/cta_card.py | 6 +++--- .../src/reflex_site_shared/views/marketing_navbar.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/app/assets/tailwind-theme.css b/docs/app/assets/tailwind-theme.css index ee145087d28..4a555afa94c 100644 --- a/docs/app/assets/tailwind-theme.css +++ b/docs/app/assets/tailwind-theme.css @@ -1239,7 +1239,7 @@ /* Width */ --layout-max-width: 81rem; --docs-layout-max-width: 69rem; - --landing-layout-max-width: 68.5rem; + --landing-layout-max-width: 78.5rem; @keyframes accordion-down { from { diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py b/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py index eed8cd456ab..16803ae025e 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/cta_card.py @@ -43,13 +43,13 @@ def cta_card(): ), class_name="flex flex-row gap-4 items-center", ), - class_name="flex flex-col gap-6 justify-center max-w-[24.5rem]", + class_name="flex flex-col gap-6 justify-center max-w-[29.25rem]", ), rx.image( - f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/cta_gray_lines.svg", + f"{REFLEX_ASSETS_CDN}common/{rx.color_mode_cond('light', 'dark')}/cta_gray_lines_2.svg", class_name="w-auto h-full pointer-events-none", loading="lazy", alt="CTA Card", ), - class_name="flex flex-row justify-between max-w-(--docs-layout-max-width) mx-auto w-full bg-white/96 dark:bg-m-slate-11 backdrop-blur-[16px] rounded-xl relative overflow-hidden shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_12px_24px_0_rgba(0,0,0,0.08),0_1px_1px_0_rgba(0,0,0,0.01),0_4px_8px_0_rgba(0,0,0,0.03)] dark:shadow-none dark:border dark:border-m-slate-9 pl-16 max-lg:hidden mb-12 mt-24", + class_name="flex flex-row justify-between max-w-(--landing-layout-max-width) mx-auto w-full bg-white/96 dark:bg-m-slate-11 backdrop-blur-[16px] rounded-xl relative overflow-hidden shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_12px_24px_0_rgba(0,0,0,0.08),0_1px_1px_0_rgba(0,0,0,0.01),0_4px_8px_0_rgba(0,0,0,0.03)] dark:shadow-none dark:border dark:border-m-slate-9 pl-16 max-lg:hidden mb-12 mt-24", ) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py index ef069d276a1..cb36206a9a7 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py @@ -806,7 +806,7 @@ def navigation_menu() -> rx.Component: class_name="safari-nav-positioner box-border h-[var(--positioner-height)] w-[var(--positioner-width)] max-w-[var(--available-width)] transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-[instant]:transition-none", side_offset=30, align="start", - align_offset=-105, + align_offset=-106, position_method="fixed", ), ), @@ -829,7 +829,7 @@ def marketing_navbar() -> rx.Component: rx.el.header( logo(), navigation_menu(), - class_name="w-full max-w-[71.5rem] h-[4.5rem] mx-auto flex flex-row items-center p-5 rounded-b-xl backdrop-blur-[16px] shadow-[0_-2px_2px_1px_rgba(0,0,0,0.02),0_1px_1px_0_rgba(0,0,0,0.08),0_4px_8px_0_rgba(0,0,0,0.03),0_0_0_1px_#FFF_inset] dark:shadow-none dark:border-x dark:border-b dark:border-m-slate-10 bg-gradient-to-b from-white to-m-slate-1 dark:from-m-slate-11 dark:to-m-slate-12", + class_name="w-full max-w-[81rem] h-[4.5rem] mx-auto flex flex-row items-center p-5 rounded-b-xl backdrop-blur-[16px] shadow-[0_-2px_2px_1px_rgba(0,0,0,0.02),0_1px_1px_0_rgba(0,0,0,0.08),0_4px_8px_0_rgba(0,0,0,0.03),0_0_0_1px_#FFF_inset] dark:shadow-none dark:border-x dark:border-b dark:border-m-slate-10 bg-gradient-to-b from-white to-m-slate-1 dark:from-m-slate-11 dark:to-m-slate-12", ), class_name="flex flex-col w-full top-0 z-[9999] fixed self-center", ) From 3cadc6722dceeca0feaaf2ffef7a33d987e496a0 Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 11 May 2026 15:59:28 +0200 Subject: [PATCH 10/14] bit more offset --- .../src/reflex_site_shared/pages/page404.py | 13 ++++++------- .../reflex_site_shared/views/marketing_navbar.py | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/pages/page404.py b/packages/reflex-site-shared/src/reflex_site_shared/pages/page404.py index 6f84c6211c7..a685adf4da3 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/pages/page404.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/pages/page404.py @@ -4,12 +4,6 @@ from reflex_site_shared.components.blocks.flexdown import markdown_with_shiki from reflex_site_shared.templates.webpage import webpage -contents = f""" -# Page Not Found - -The page at `{rx.State.router.page.raw_path}` doesn't exist. -""" - @webpage(path="/404", title="Page Not Found · Reflex.dev", add_as_page=False) def page404(): @@ -19,6 +13,11 @@ def page404(): The component. """ return rx.box( - markdown_with_shiki(contents), + markdown_with_shiki("# Page Not Found"), + rx.el.p( + "The page at ", + rx.code(rx.State.router.page.raw_path, class_name="code-style"), + " doesn't exist.", + ), class_name="h-[80vh] w-full flex flex-col items-center justify-center", ) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py index cb36206a9a7..7d931ca94e3 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py @@ -806,7 +806,7 @@ def navigation_menu() -> rx.Component: class_name="safari-nav-positioner box-border h-[var(--positioner-height)] w-[var(--positioner-width)] max-w-[var(--available-width)] transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-[instant]:transition-none", side_offset=30, align="start", - align_offset=-106, + align_offset=-109, position_method="fixed", ), ), From af4633901467564856da14b63395880b56af0241 Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 11 May 2026 17:31:40 +0200 Subject: [PATCH 11/14] navbar updates --- .../src/reflex_site_shared/constants.py | 2 +- .../src/reflex_site_shared/views/marketing_navbar.py | 9 ++++++--- .../src/reflex_site_shared/views/sidebar/__init__.py | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/constants.py b/packages/reflex-site-shared/src/reflex_site_shared/constants.py index 850ec53b1e0..1c2379b1b86 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/constants.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/constants.py @@ -34,7 +34,7 @@ "REFLEX_DEV_WEB_GENERAL_FORM_FEEDBACK_WEBHOOK_URL", "" ) RECENT_BLOGS_API_URL: str = os.environ.get( - "RECENT_BLOGS_API_URL", "https://reflex.dev/api/v1/recent-blogs" + "RECENT_BLOGS_API_URL", "https://reflex.dev/blog-api/api/v1/recent-blogs" ) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py index 7d931ca94e3..929c31f550b 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py @@ -130,8 +130,11 @@ def products_column_gutter() -> rx.Component: The component. """ return rx.el.div( + rx.el.div( + class_name="pointer-events-none absolute top-0 left-0 h-12 w-full border-b border-secondary-4", + ), role="presentation", - class_name="w-8 shrink-0 bg-secondary-1 border-secondary-4 border-x", + class_name="w-8 shrink-0 bg-secondary-1 border-secondary-4 border-x relative", ) @@ -193,7 +196,7 @@ def products_ship_column_body() -> rx.Component: ), rx.el.div( rx.image( - src=f"{REFLEX_ASSETS_CDN}landing/features/{rx.color_mode_cond('light', 'dark')}/ship_navbar_2.svg", + src=f"{REFLEX_ASSETS_CDN}landing/features/{rx.color_mode_cond('light', 'dark')}/ship_navbar_3.svg", alt="Deploy, monitor & scale", loading="lazy", class_name="h-auto w-full max-w-full object-cover", @@ -299,7 +302,7 @@ def products_content() -> rx.Component: class_name="flex min-h-0 w-full flex-row bg-white-1", ), products_menu_footer(), - class_name="flex max-w-[min(100vw-2rem,1080px)] w-[1080px] flex-col overflow-hidden rounded-xl bg-secondary-1 dark:shadow-card-dark", + class_name="flex max-w-[min(100vw-2rem,1240px)] w-[1240px] flex-col overflow-hidden rounded-xl bg-secondary-1 dark:shadow-card-dark", ), class_name="p-0", ) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py b/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py index 3b78a151be1..22984658324 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/sidebar/__init__.py @@ -194,7 +194,7 @@ def navbar_sidebar_button() -> rx.Component: }, ), size="icon-sm", - variant="outline", + variant="outline-shadow", custom_attrs={"aria-label": "Open sidebar"}, native_button=False, ), From 2f1431e28ed2a3bbf682781bee89edc6bf34928e Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 11 May 2026 17:35:55 +0200 Subject: [PATCH 12/14] no gap --- .../src/reflex_site_shared/views/marketing_navbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py index 929c31f550b..7ec5080dd91 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py @@ -172,7 +172,7 @@ def products_iterate_column_body() -> rx.Component: target="_blank", class_name="flex flex-row items-center gap-2 text-sm font-medium text-secondary-12 hover:text-primary-10 dark:hover:text-primary-9 group mt-auto", ), - class_name="flex flex-col gap-2 p-6 text-center justify-center items-center min-h-[252px] h-full", + class_name="flex flex-col p-6 text-center justify-center items-center min-h-[252px] h-full", ) From d6aab7eb3492bc9cd6b6c6a4806788b4ab146c8d Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 11 May 2026 17:40:46 +0200 Subject: [PATCH 13/14] more gaps update --- .../src/reflex_site_shared/views/marketing_navbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py index 7ec5080dd91..dc812a53e15 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/views/marketing_navbar.py @@ -261,7 +261,7 @@ def products_build_column_body() -> rx.Component: href="/docs/ai/integrations/ai-onboarding/", class_name="group flex flex-col gap-2 items-center text-center", ), - class_name="flex flex-col gap-2 text-center justify-center items-center min-h-[252px]", + class_name="flex flex-col text-center justify-center items-center min-h-[252px]", ) From 624b323457af601e44e9cf0a7405403b2ed179d7 Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 11 May 2026 17:57:14 +0200 Subject: [PATCH 14/14] update max -w --- docs/app/reflex_docs/pages/docs_landing/views/ai_builder.py | 2 +- docs/app/reflex_docs/pages/docs_landing/views/divider.py | 2 +- docs/app/reflex_docs/pages/docs_landing/views/enterprise.py | 2 +- docs/app/reflex_docs/pages/docs_landing/views/framework.py | 2 +- docs/app/reflex_docs/pages/docs_landing/views/hero.py | 2 +- docs/app/reflex_docs/pages/docs_landing/views/hosting.py | 2 +- docs/app/reflex_docs/pages/docs_landing/views/other.py | 2 +- docs/app/reflex_docs/pages/docs_landing/views/self_hosting.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/app/reflex_docs/pages/docs_landing/views/ai_builder.py b/docs/app/reflex_docs/pages/docs_landing/views/ai_builder.py index f45e3c13632..52bb83c635e 100644 --- a/docs/app/reflex_docs/pages/docs_landing/views/ai_builder.py +++ b/docs/app/reflex_docs/pages/docs_landing/views/ai_builder.py @@ -183,7 +183,7 @@ def ai_builder_section() -> rx.Component: ), class_name="grid grid-cols-1 lg:grid-cols-3 gap-12", ), - class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto", + class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto", ), class_name="bg-gradient-to-b from-white-1 to-m-slate-1 dark:from-m-slate-11 dark:to-m-slate-12 w-full lg:pt-24 lg:pb-24 pb-10 max-xl:px-6 max-lg:pt-10", ) diff --git a/docs/app/reflex_docs/pages/docs_landing/views/divider.py b/docs/app/reflex_docs/pages/docs_landing/views/divider.py index 0468cc4bc3f..1796075ba6f 100644 --- a/docs/app/reflex_docs/pages/docs_landing/views/divider.py +++ b/docs/app/reflex_docs/pages/docs_landing/views/divider.py @@ -11,7 +11,7 @@ def divider(class_name: str = "") -> rx.Component: class_name="absolute top-0 -left-24 w-24 h-px bg-gradient-to-r from-transparent to-current text-m-slate-4 dark:text-m-slate-10" ), class_name=ui.cn( - "w-full h-[1px] bg-m-slate-4 dark:bg-m-slate-10 relative max-w-(--docs-layout-max-width) mx-auto", + "w-full h-[1px] bg-m-slate-4 dark:bg-m-slate-10 relative max-w-(--landing-layout-max-width) mx-auto", class_name, ), ) diff --git a/docs/app/reflex_docs/pages/docs_landing/views/enterprise.py b/docs/app/reflex_docs/pages/docs_landing/views/enterprise.py index 419fce7237c..3ed024c832b 100644 --- a/docs/app/reflex_docs/pages/docs_landing/views/enterprise.py +++ b/docs/app/reflex_docs/pages/docs_landing/views/enterprise.py @@ -34,5 +34,5 @@ def enterprise_section() -> rx.Component: ), class_name="grid grid-cols-1 lg:grid-cols-2 border-t border-secondary-4 relative", ), - class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden", + class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden", ) diff --git a/docs/app/reflex_docs/pages/docs_landing/views/framework.py b/docs/app/reflex_docs/pages/docs_landing/views/framework.py index aad79932cff..e7d46eba0f5 100644 --- a/docs/app/reflex_docs/pages/docs_landing/views/framework.py +++ b/docs/app/reflex_docs/pages/docs_landing/views/framework.py @@ -187,5 +187,5 @@ def framework() -> rx.Component: components_section(), class_name="flex flex-col lg:flex-row relative", ), - class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden", + class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden", ) diff --git a/docs/app/reflex_docs/pages/docs_landing/views/hero.py b/docs/app/reflex_docs/pages/docs_landing/views/hero.py index 70b440b6e53..42d9fba2c01 100644 --- a/docs/app/reflex_docs/pages/docs_landing/views/hero.py +++ b/docs/app/reflex_docs/pages/docs_landing/views/hero.py @@ -61,6 +61,6 @@ def hero() -> rx.Component: ), ), class_name=ui.cn( - "flex lg:flex-row flex-col max-w-(--docs-layout-max-width) mx-auto w-full max-lg:pb-10 max-xl:px-6", + "flex lg:flex-row flex-col max-w-(--landing-layout-max-width) mx-auto w-full max-lg:pb-10 max-xl:px-6", ), ) diff --git a/docs/app/reflex_docs/pages/docs_landing/views/hosting.py b/docs/app/reflex_docs/pages/docs_landing/views/hosting.py index 4c7bfba9d5d..4851decde7a 100644 --- a/docs/app/reflex_docs/pages/docs_landing/views/hosting.py +++ b/docs/app/reflex_docs/pages/docs_landing/views/hosting.py @@ -47,5 +47,5 @@ def hosting_section() -> rx.Component: ), class_name="grid grid-cols-1 lg:grid-cols-2 border-t border-m-slate-4 dark:border-m-slate-10 relative", ), - class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto w-full justify-start max-xl:px-6 lg:mb-24 overflow-hidden", + class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto w-full justify-start max-xl:px-6 lg:mb-24 overflow-hidden", ) diff --git a/docs/app/reflex_docs/pages/docs_landing/views/other.py b/docs/app/reflex_docs/pages/docs_landing/views/other.py index 3a9172779c5..f3945d82c7d 100644 --- a/docs/app/reflex_docs/pages/docs_landing/views/other.py +++ b/docs/app/reflex_docs/pages/docs_landing/views/other.py @@ -35,5 +35,5 @@ def other_section() -> rx.Component: faded_borders(), class_name="grid grid-cols-1 lg:grid-cols-2 border-t border-secondary-4 relative", ), - class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden", + class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden", ) diff --git a/docs/app/reflex_docs/pages/docs_landing/views/self_hosting.py b/docs/app/reflex_docs/pages/docs_landing/views/self_hosting.py index da8ddba27e4..6f94f974f6e 100644 --- a/docs/app/reflex_docs/pages/docs_landing/views/self_hosting.py +++ b/docs/app/reflex_docs/pages/docs_landing/views/self_hosting.py @@ -34,5 +34,5 @@ def self_hosting_section() -> rx.Component: ), class_name="grid grid-cols-1 lg:grid-cols-2 border-t border-secondary-4 relative", ), - class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--docs-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden max-lg:pt-10", + class_name="flex flex-col gap-10 max-lg:text-center relative max-w-(--landing-layout-max-width) mx-auto w-full justify-start lg:mb-24 mb-10 max-xl:px-6 overflow-hidden max-lg:pt-10", )