From a46ebb29d11c5fd2317c8526b85c3f6555251d2b Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 23 Feb 2026 11:31:15 +0100 Subject: [PATCH 1/6] ENG-8877: Low code page --- assets/common/dark/grid.svg | 1 + assets/common/light/grid.svg | 1 + assets/logos/dark/gradient_r.svg | 1 + assets/logos/light/gradient_r.svg | 1 + assets/migration/dark/accenture.svg | 1 + assets/migration/dark/autodesk.svg | 1 + assets/migration/dark/fastly.svg | 1 + assets/migration/dark/open_sea.svg | 1 + assets/migration/dark/plotly.svg | 23 +++ assets/migration/dark/powerbi.svg | 9 + assets/migration/dark/retool.svg | 3 + assets/migration/dark/streamlit.svg | 12 ++ assets/migration/light/accenture.svg | 1 + assets/migration/light/autodesk.svg | 1 + assets/migration/light/fastly.svg | 1 + assets/migration/light/open_sea.svg | 1 + assets/migration/light/plotly.svg | 23 +++ assets/migration/light/powerbi.svg | 9 + assets/migration/light/retool.svg | 3 + assets/migration/light/streamlit.svg | 12 ++ pcweb/pages/__init__.py | 1 + pcweb/pages/hosting/hosting.py | 2 - pcweb/pages/migration/low_code/__init__.py | 44 ++++ .../migration/low_code/views/__init__.py | 5 + .../pages/migration/low_code/views/compare.py | 188 ++++++++++++++++++ pcweb/pages/migration/low_code/views/hero.py | 104 ++++++++++ .../pages/migration/low_code/views/quotes.py | 140 +++++++++++++ pcweb/views/marketing_navbar.py | 6 +- 28 files changed, 593 insertions(+), 3 deletions(-) create mode 100644 assets/common/dark/grid.svg create mode 100644 assets/common/light/grid.svg create mode 100644 assets/logos/dark/gradient_r.svg create mode 100644 assets/logos/light/gradient_r.svg create mode 100644 assets/migration/dark/accenture.svg create mode 100644 assets/migration/dark/autodesk.svg create mode 100644 assets/migration/dark/fastly.svg create mode 100644 assets/migration/dark/open_sea.svg create mode 100644 assets/migration/dark/plotly.svg create mode 100644 assets/migration/dark/powerbi.svg create mode 100644 assets/migration/dark/retool.svg create mode 100644 assets/migration/dark/streamlit.svg create mode 100644 assets/migration/light/accenture.svg create mode 100644 assets/migration/light/autodesk.svg create mode 100644 assets/migration/light/fastly.svg create mode 100644 assets/migration/light/open_sea.svg create mode 100644 assets/migration/light/plotly.svg create mode 100644 assets/migration/light/powerbi.svg create mode 100644 assets/migration/light/retool.svg create mode 100644 assets/migration/light/streamlit.svg create mode 100644 pcweb/pages/migration/low_code/__init__.py create mode 100644 pcweb/pages/migration/low_code/views/__init__.py create mode 100644 pcweb/pages/migration/low_code/views/compare.py create mode 100644 pcweb/pages/migration/low_code/views/hero.py create mode 100644 pcweb/pages/migration/low_code/views/quotes.py diff --git a/assets/common/dark/grid.svg b/assets/common/dark/grid.svg new file mode 100644 index 000000000..36b00d921 --- /dev/null +++ b/assets/common/dark/grid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/common/light/grid.svg b/assets/common/light/grid.svg new file mode 100644 index 000000000..c64061633 --- /dev/null +++ b/assets/common/light/grid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/logos/dark/gradient_r.svg b/assets/logos/dark/gradient_r.svg new file mode 100644 index 000000000..4ece52782 --- /dev/null +++ b/assets/logos/dark/gradient_r.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/logos/light/gradient_r.svg b/assets/logos/light/gradient_r.svg new file mode 100644 index 000000000..4ece52782 --- /dev/null +++ b/assets/logos/light/gradient_r.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/accenture.svg b/assets/migration/dark/accenture.svg new file mode 100644 index 000000000..c12408f9c --- /dev/null +++ b/assets/migration/dark/accenture.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/autodesk.svg b/assets/migration/dark/autodesk.svg new file mode 100644 index 000000000..6b37b678c --- /dev/null +++ b/assets/migration/dark/autodesk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/fastly.svg b/assets/migration/dark/fastly.svg new file mode 100644 index 000000000..dc7eb0439 --- /dev/null +++ b/assets/migration/dark/fastly.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/open_sea.svg b/assets/migration/dark/open_sea.svg new file mode 100644 index 000000000..dc8f1391e --- /dev/null +++ b/assets/migration/dark/open_sea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/plotly.svg b/assets/migration/dark/plotly.svg new file mode 100644 index 000000000..8bd721101 --- /dev/null +++ b/assets/migration/dark/plotly.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/migration/dark/powerbi.svg b/assets/migration/dark/powerbi.svg new file mode 100644 index 000000000..9433ff6e8 --- /dev/null +++ b/assets/migration/dark/powerbi.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/assets/migration/dark/retool.svg b/assets/migration/dark/retool.svg new file mode 100644 index 000000000..b76379595 --- /dev/null +++ b/assets/migration/dark/retool.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/assets/migration/dark/streamlit.svg b/assets/migration/dark/streamlit.svg new file mode 100644 index 000000000..d9a304a55 --- /dev/null +++ b/assets/migration/dark/streamlit.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/migration/light/accenture.svg b/assets/migration/light/accenture.svg new file mode 100644 index 000000000..c12408f9c --- /dev/null +++ b/assets/migration/light/accenture.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/autodesk.svg b/assets/migration/light/autodesk.svg new file mode 100644 index 000000000..6b37b678c --- /dev/null +++ b/assets/migration/light/autodesk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/fastly.svg b/assets/migration/light/fastly.svg new file mode 100644 index 000000000..dc7eb0439 --- /dev/null +++ b/assets/migration/light/fastly.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/open_sea.svg b/assets/migration/light/open_sea.svg new file mode 100644 index 000000000..dc8f1391e --- /dev/null +++ b/assets/migration/light/open_sea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/plotly.svg b/assets/migration/light/plotly.svg new file mode 100644 index 000000000..8bd721101 --- /dev/null +++ b/assets/migration/light/plotly.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/migration/light/powerbi.svg b/assets/migration/light/powerbi.svg new file mode 100644 index 000000000..9433ff6e8 --- /dev/null +++ b/assets/migration/light/powerbi.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/assets/migration/light/retool.svg b/assets/migration/light/retool.svg new file mode 100644 index 000000000..b76379595 --- /dev/null +++ b/assets/migration/light/retool.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/assets/migration/light/streamlit.svg b/assets/migration/light/streamlit.svg new file mode 100644 index 000000000..d9a304a55 --- /dev/null +++ b/assets/migration/light/streamlit.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/pcweb/pages/__init__.py b/pcweb/pages/__init__.py index 15c76d619..6a4d58872 100644 --- a/pcweb/pages/__init__.py +++ b/pcweb/pages/__init__.py @@ -20,6 +20,7 @@ from .meeting_successfully_booked import ( page_meeting_successfully_booked as page_meeting_successfully_booked, ) +from .migration.low_code import low_code_migration_page as low_code_migration_page from .page404 import page404 as page404 from .pricing.pricing import pricing as pricing from .sales import sales as sales diff --git a/pcweb/pages/hosting/hosting.py b/pcweb/pages/hosting/hosting.py index fb37aba4c..4add630ce 100644 --- a/pcweb/pages/hosting/hosting.py +++ b/pcweb/pages/hosting/hosting.py @@ -8,7 +8,6 @@ from .views.features import features from .views.hero import hero from .views.preview import preview -from .views.pricing_cards import pricing_cards from .views.templates import templates @@ -21,7 +20,6 @@ def hosting_landing() -> rx.Component: preview(), deploy_animation(), features(), - pricing_cards(), templates(), class_name="flex flex-col size-full justify-center items-center", ) diff --git a/pcweb/pages/migration/low_code/__init__.py b/pcweb/pages/migration/low_code/__init__.py new file mode 100644 index 000000000..60a67bb14 --- /dev/null +++ b/pcweb/pages/migration/low_code/__init__.py @@ -0,0 +1,44 @@ +import reflex as rx +import reflex_ui as ui + +from pcweb.components.hosting_banner import HostingBannerState +from pcweb.meta.meta import create_meta_tags +from pcweb.pages.about.views.divider import divider +from pcweb.pages.framework.views.footer_index import footer_index +from pcweb.pages.migration.low_code.views import compare, hero, quotes +from pcweb.views.marketing_navbar import marketing_navbar + + +@rx.page( + route="/migration/low-code", + title="Switch from Low Code to Reflex", + meta=create_meta_tags( + title="Switch from Low Code to Reflex", + description="Switch from Low Code to Reflex - The platform to build and scale enterprise apps", + image="/previews/low_code_migration_preview.webp", + ), +) +def low_code_migration_page() -> rx.Component: + return rx.el.div( + marketing_navbar(), + rx.el.main( + rx.el.div( + hero(), + quotes(), + divider(), + compare(), + divider(), + footer_index(), + class_name="flex flex-col relative justify-center items-center w-full", + ), + class_name="flex flex-col w-full relative h-full justify-center items-center", + ), + class_name=ui.cn( + "flex flex-col w-full justify-center items-center relative dark:bg-m-slate-12 bg-m-slate-1", + rx.cond( + HostingBannerState.is_banner_visible, + "lg:pt-[7rem] pt-[3.5rem]", + "lg:pt-[4.5rem] pt-[3.5rem]", + ), + ), + ) diff --git a/pcweb/pages/migration/low_code/views/__init__.py b/pcweb/pages/migration/low_code/views/__init__.py new file mode 100644 index 000000000..7ad7dc9ad --- /dev/null +++ b/pcweb/pages/migration/low_code/views/__init__.py @@ -0,0 +1,5 @@ +from .compare import compare +from .hero import hero +from .quotes import quotes + +__all__ = ["compare", "hero", "quotes"] diff --git a/pcweb/pages/migration/low_code/views/compare.py b/pcweb/pages/migration/low_code/views/compare.py new file mode 100644 index 000000000..9f3125181 --- /dev/null +++ b/pcweb/pages/migration/low_code/views/compare.py @@ -0,0 +1,188 @@ +import reflex as rx +import reflex_ui as ui + + +def comparison_title(title: str, icon: str) -> rx.Component: + return rx.el.div( + ui.icon(icon, stroke_width=1.5, class_name="shrink-0 lg:size-7 size-6"), + rx.el.span( + title, + class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-lg text-base font-[575]", + ), + class_name="flex flex-row items-center gap-3 lg:p-12 p-6 border-y border-r border-m-slate-4 dark:border-m-slate-10", + ) + + +def pros_card(pros: list[str]) -> rx.Component: + return rx.el.ul( + *[ + rx.el.li( + ui.icon( + "Tick02Icon", + class_name="shrink-0 text-primary-9 dark:text-primary-10", + ), + rx.el.span( + pro, + class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525]", + ), + class_name="flex flex-row items-center gap-2.5", + ) + for pro in pros + ], + class_name="list-inside flex flex-col gap-2 lg:p-12 p-6 [box-shadow:0_0_0_1px_rgba(0,_0,_0,_0.12)_inset,_0_6px_12px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_4px_6px_0_rgba(0,_0,_0,_0.02)] dark:shadow-none rounded-xl bg-white-1 dark:bg-m-slate-11 w-full", + ) + + +def cons_card(cons: list[str]) -> rx.Component: + return rx.el.ul( + *[ + rx.el.li( + ui.icon( + "MultiplicationSignIcon", + stroke_width=1.5, + class_name="shrink-0 text-m-slate-7 dark:text-m-slate-6", + ), + rx.el.span( + con, + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[525]", + ), + class_name="flex flex-row items-center gap-2.5", + ) + for con in cons + ], + class_name="list-inside flex flex-col gap-2 lg:p-12 p-6 w-full lg:border-x border-l border-m-slate-4 dark:border-m-slate-10", + ) + + +def pros_cons_cards(pros: list[str], cons: list[str]) -> rx.Component: + return rx.el.div( + pros_card(pros), + cons_card(cons), + class_name="grid lg:grid-cols-2 grid-cols-1 max-lg:border-r", + ) + + +def top_title(title: str) -> rx.Component: + return rx.el.span( + title, + class_name="text-m-slate-12 dark:text-m-slate-3 text-xs leading-[1.5rem] font-medium font-mono border-r border-m-slate-4 dark:border-m-slate-10 lg:px-12 lg:py-3 p-6 bg-secondary-1 dark:bg-m-slate-10 border-t", + ) + + +def comparison_cards() -> rx.Component: + return rx.el.div( + rx.el.div( + top_title("Reflex"), + top_title("Bubble, Retool, Webflow, etc."), + class_name="grid grid-cols-2", + ), + comparison_title("Full Control Without the Ceiling", "CodesandboxIcon"), + pros_cons_cards( + [ + "Full control over your code", + "No limits on complexity", + "Customizable templates", + ], + [ + "Limited control over your code", + "No limits on complexity", + "Customizable templates", + ], + ), + comparison_title("Your own code", "SourceCodeSquareIcon"), + pros_cons_cards( + [ + "Full control over your code", + "No limits on complexity", + "Customizable templates", + ], + [ + "Limited control over your code", + "No limits on complexity", + "Customizable templates", + ], + ), + comparison_title("Python Ecosystem Access", "PythonIcon"), + pros_cons_cards( + [ + "Full control over your code", + "No limits on complexity", + "Customizable templates", + ], + [ + "Limited control over your code", + "No limits on complexity", + "Customizable templates", + ], + ), + comparison_title("Scales With Complexity", "SquareArrowExpand02Icon"), + pros_cons_cards( + [ + "Full control over your code", + "No limits on complexity", + "Customizable templates", + ], + [ + "Limited control over your code", + "No limits on complexity", + "Customizable templates", + ], + ), + comparison_title( + "Team Collaboration & Engineering Practices", "UserSwitchIcon" + ), + pros_cons_cards( + [ + "Full control over your code", + "No limits on complexity", + "Customizable templates", + ], + [ + "Limited control over your code", + "No limits on complexity", + "Customizable templates", + ], + ), + rx.el.div( + class_name="absolute -top-24 right-0 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( + class_name="absolute -top-24 -left-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( + class_name="absolute -bottom-24 right-0 w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10" + ), + rx.el.div( + class_name="absolute -bottom-24 -left-px w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10" + ), + class_name="flex flex-col w-full max-w-[45rem] ml-auto border-l border-m-slate-4 dark:border-m-slate-10 mt-18 border-b mb-24 relative", + ) + + +def compare() -> rx.Component: + return rx.el.section( + rx.el.div( + rx.el.div( + rx.el.p( + "Compare", + class_name="text-sm font-[525] text-primary-10 max-lg:text-center dark:text-m-slate-6", + ), + rx.el.h1( + "How You Benefit ", + rx.el.br(class_name="max-lg:hidden"), + " With Reflex to ", + rx.el.br(class_name="max-lg:hidden"), + " Other Approaches", + class_name="text-m-slate-12 dark:text-m-slate-3 text-3xl font-[575]", + ), + rx.el.h2( + "Reflex is growing-and we're looking for people who care deeply about developer experience, clean abstractions, and shipping things that matter.", + class_name="text-m-slate-7 dark:text-m-slate-6 text-base font-[475]", + ), + class_name="flex flex-col gap-6 lg:max-w-[18rem] lg:sticky lg:top-[11rem] lg:self-start max-lg:self-center max-lg:items-center max-lg:text-center", + ), + comparison_cards(), + class_name="flex lg:flex-row flex-col max-lg:gap-6 max-w-(--docs-layout-max-width) mx-auto relative py-24 max-lg:px-6", + ), + 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 relative", + ) diff --git a/pcweb/pages/migration/low_code/views/hero.py b/pcweb/pages/migration/low_code/views/hero.py new file mode 100644 index 000000000..0f03410a4 --- /dev/null +++ b/pcweb/pages/migration/low_code/views/hero.py @@ -0,0 +1,104 @@ +import reflex as rx +import reflex_ui as ui +from reflex_ui.blocks.demo_form import demo_form_dialog + +from pcweb.components.marketing_button import button + + +def floating_logo(src: str, alt: str, class_name: str = "") -> rx.Component: + return rx.el.div( + rx.image( + src=src, + alt=alt, + loading="eager", + custom_attrs={"fetchPriority": "high"}, + ), + class_name=ui.cn( + class_name, + "absolute left-1/2 -translate-x-1/2 -translate-y-1/2 z-[-1] pointer-events-none size-16 rounded-[1rem] bg-m-slate-1 dark:bg-m-slate-12 [box-shadow:0_1px_0_0_#FFF_inset,_0_0_0_1px_rgba(0,_0,_0,_0.12),_0_8px_16px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_4px_8px_0_rgba(0,_0,_0,_0.02)] dark:shadow-none dark:border dark:border-m-slate-9 flex items-center justify-center", + ), + ) + + +def gradient_logo() -> rx.Component: + return rx.el.div( + rx.image( + src=f"/logos/{rx.color_mode_cond('light', 'dark')}/gradient_r.svg", + alt="Gradient Reflex Logo", + loading="eager", + custom_attrs={"fetchPriority": "high"}, + ), + class_name="size-24 rounded-[1rem] bg-gradient-to-b from-m-slate-2 to-white-1 dark:from-m-slate-11 dark:to-m-slate-12 [box-shadow:0_1px_0_0_#FFF_inset,_0_0_0_1px_rgba(0,_0,_0,_0.12),_0_8px_16px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_4px_8px_0_rgba(0,_0,_0,_0.02)] dark:shadow-none dark:border dark:border-m-slate-9 flex items-center justify-center absolute left-1/2 -translate-x-1/2 -translate-y-1/2 z-[1] pointer-events-none top-[13.5rem]", + ) + + +def hero() -> rx.Component: + return rx.el.section( + rx.el.div( + rx.el.div( + rx.image( + src=f"/common/{rx.color_mode_cond('light', 'dark')}/grid.svg", + alt="Grid", + loading="eager", + custom_attrs={"fetchPriority": "high"}, + class_name=ui.cn( + "absolute left-1/2 -translate-x-1/2 z-[-1] pointer-events-none top-[2.815rem]", + ), + ), + floating_logo( + src=f"/migration/{rx.color_mode_cond('light', 'dark')}/plotly.svg", + alt="Plotly Logo", + class_name="top-[9.5rem] -ml-[10.5rem]", + ), + floating_logo( + src=f"/migration/{rx.color_mode_cond('light', 'dark')}/powerbi.svg", + alt="Power BI Logo", + class_name="top-[15rem] ml-[16.5rem]", + ), + floating_logo( + src=f"/migration/{rx.color_mode_cond('light', 'dark')}/retool.svg", + alt="Retool Logo", + class_name="top-[9.5rem] ml-[10.5rem]", + ), + floating_logo( + src=f"/migration/{rx.color_mode_cond('light', 'dark')}/streamlit.svg", + alt="Streamlit Logo", + class_name="top-[15rem] -ml-[16.5rem]", + ), + gradient_logo(), + class_name="max-lg:hidden", + ), + rx.el.p( + "Move From Low Code to Reflex", + class_name="text-sm font-[525] text-primary-10 dark:text-m-slate-6", + ), + rx.el.h1( + "The Next-Gen Platform Built for Modern Enterprises", + class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-5xl text-3xl font-[575]", + ), + rx.el.h2( + "Escape low-code constraints without sacrificing speed. Build production-grade apps in pure Python with complete control over your stack.", + class_name="text-m-slate-7 dark:text-m-slate-6 text-base font-[475]", + ), + demo_form_dialog( + trigger=button( + "Book a Demo", + variant="primary", + size="lg", + native_button=False, + ), + ), + rx.el.div( + class_name="absolute -bottom-px -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10" + ), + rx.el.div( + class_name="absolute -bottom-px -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( + "flex flex-col gap-6 items-center justify-center text-center max-w-[45rem] pb-16 border-b border-m-slate-4 dark:border-m-slate-10 relative isolate lg:pt-[21.75rem] pt-[10.5rem]", + ), + ), + class_name=ui.cn( + "flex lg:flex-row flex-col max-w-(--layout-max-width) mx-auto lg:px-24 px-6 overflow-hidden", + ), + ) diff --git a/pcweb/pages/migration/low_code/views/quotes.py b/pcweb/pages/migration/low_code/views/quotes.py new file mode 100644 index 000000000..e0e18e90a --- /dev/null +++ b/pcweb/pages/migration/low_code/views/quotes.py @@ -0,0 +1,140 @@ +from enum import StrEnum +from typing import TypedDict + +import reflex as rx +import reflex_ui as ui +from reflex.experimental import ClientStateVar + + +class Companies(StrEnum): + OPEN_SEA = "open_sea" + FASTLY = "fastly" + AUTODESK = "autodesk" + ACCENTURE = "accenture" + + +class CompanyInfo(TypedDict): + name: str + title: str + quote: str + + +COMPANIES_INFO: dict[Companies, CompanyInfo] = { + Companies.OPEN_SEA: { + "name": "Alex Atallah", + "title": "Co-founder & CEO, OpenSea", + "image": "/landing/social/alex_opensea.webp", + "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", + }, + Companies.FASTLY: { + "name": "Alex Atallah", + "title": "Co-founder & CEO, OpenSea", + "image": "/landing/social/alex_opensea.webp", + "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", + }, + Companies.AUTODESK: { + "name": "Alex Atallah", + "title": "Co-founder & CEO, OpenSea", + "image": "/landing/social/alex_opensea.webp", + "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", + }, + Companies.ACCENTURE: { + "name": "Alex Atallah", + "title": "Co-founder & CEO, OpenSea", + "image": "/landing/social/alex_opensea.webp", + "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", + }, +} + + +active_company_cs = ClientStateVar.create( + "active_company_cs", default=Companies.OPEN_SEA +) + + +def company_card(company: Companies) -> rx.Component: + return rx.el.button( + rx.image( + src=f"/migration/{rx.color_mode_cond('light', 'dark')}/{company}.svg", + alt=f"{company} logo", + loading="lazy", + class_name=ui.cn( + "transition-[filter] group-hover:brightness-0 dark:group-hover:brightness-[10]", + rx.cond( + active_company_cs.value == company, + "brightness-0 dark:brightness-[10]", + "", + ), + ), + ), + aria_label=f"Company: {company}", + on_click=active_company_cs.set_value(company), + class_name=ui.cn( + "flex justify-end items-center h-12 py-3.5 lg:pr-12 pr-3.5 group", + rx.cond( + active_company_cs.value == company, + "lg:shadow-[1px_0_0_0_var(--m-slate-12)] lg:dark:shadow-[1px_0_0_0_var(--m-slate-3)]", + "", + ), + ), + ) + + +def quote_card(company: Companies) -> rx.Component: + return rx.el.div( + rx.el.p( + COMPANIES_INFO[company]["quote"], + class_name="text-m-slate-12 dark:text-m-slate-3 text-lg font-[575] text-pretty", + ), + rx.el.div( + ui.gradient_profile( + seed=COMPANIES_INFO[company]["name"], + class_name="size-6 rounded-full", + ) + if not COMPANIES_INFO[company]["image"] + else rx.image( + src=COMPANIES_INFO[company]["image"], + loading="lazy", + alt=f"{company} logo", + class_name="size-6 rounded-full", + ), + rx.el.span( + COMPANIES_INFO[company]["name"], + class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525]", + ), + rx.el.span( + COMPANIES_INFO[company]["title"], + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[525]", + ), + class_name="flex flex-row items-center gap-3", + ), + class_name="flex flex-col gap-8 lg:px-12 lg:pt-16 p-6 lg:w-[33rem] w-full", + ) + + +def companies_column() -> rx.Component: + return rx.el.div( + company_card(Companies.OPEN_SEA), + company_card(Companies.FASTLY), + company_card(Companies.AUTODESK), + company_card(Companies.ACCENTURE), + rx.el.div( + class_name="absolute -bottom-24 -right-px w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10 max-lg:hidden" + ), + class_name="flex max-lg:px-6 max-lg:overflow-x-auto lg:flex-col flex-row gap-2 pt-13 lg:max-w-[12rem] w-full lg:border-r border-m-slate-4 dark:border-m-slate-10 relative max-lg:justify-center", + ) + + +def quotes() -> rx.Component: + return rx.el.section( + companies_column(), + rx.match( + active_company_cs.value, + (Companies.OPEN_SEA, quote_card(Companies.OPEN_SEA)), + (Companies.FASTLY, quote_card(Companies.FASTLY)), + (Companies.AUTODESK, quote_card(Companies.AUTODESK)), + (Companies.ACCENTURE, quote_card(Companies.ACCENTURE)), + quote_card(Companies.OPEN_SEA), + ), + class_name="flex lg:flex-row flex-col pb-24", + ) diff --git a/pcweb/views/marketing_navbar.py b/pcweb/views/marketing_navbar.py index 017784f63..8d56d738a 100644 --- a/pcweb/views/marketing_navbar.py +++ b/pcweb/views/marketing_navbar.py @@ -379,7 +379,11 @@ def solutions_content() -> rx.Component: solutions_column( "Migration", [ - ("Switch from No Code", "WebDesign01Icon", use_cases_page.path), + ( + "Switch from No Code", + "WebDesign01Icon", + "/migration/low-code", + ), ( "Switch from Other Frameworks", "CodeIcon", From 5279df5d79428f01c9a61995698cc92f83d44de4 Mon Sep 17 00:00:00 2001 From: Carlos Date: Mon, 23 Feb 2026 11:37:33 +0100 Subject: [PATCH 2/6] add image --- pcweb/pages/migration/low_code/views/quotes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pcweb/pages/migration/low_code/views/quotes.py b/pcweb/pages/migration/low_code/views/quotes.py index e0e18e90a..ed7d471d2 100644 --- a/pcweb/pages/migration/low_code/views/quotes.py +++ b/pcweb/pages/migration/low_code/views/quotes.py @@ -17,6 +17,7 @@ class CompanyInfo(TypedDict): name: str title: str quote: str + image: str COMPANIES_INFO: dict[Companies, CompanyInfo] = { From 61fb6b4038afec6757cb16d6c471fd50f79e3799 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 24 Feb 2026 11:33:41 +0100 Subject: [PATCH 3/6] updates --- .../pages/migration/low_code/views/compare.py | 80 +++++++++---------- .../pages/migration/low_code/views/quotes.py | 24 +++--- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/pcweb/pages/migration/low_code/views/compare.py b/pcweb/pages/migration/low_code/views/compare.py index 9f3125181..c5c4e7caf 100644 --- a/pcweb/pages/migration/low_code/views/compare.py +++ b/pcweb/pages/migration/low_code/views/compare.py @@ -19,13 +19,13 @@ def pros_card(pros: list[str]) -> rx.Component: rx.el.li( ui.icon( "Tick02Icon", - class_name="shrink-0 text-primary-9 dark:text-primary-10", + class_name="shrink-0 text-primary-9 dark:text-primary-10 h-[1.5rem]", ), rx.el.span( pro, class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525]", ), - class_name="flex flex-row items-center gap-2.5", + class_name="flex flex-row items-start gap-2.5", ) for pro in pros ], @@ -40,13 +40,13 @@ def cons_card(cons: list[str]) -> rx.Component: ui.icon( "MultiplicationSignIcon", stroke_width=1.5, - class_name="shrink-0 text-m-slate-7 dark:text-m-slate-6", + class_name="shrink-0 text-m-slate-7 dark:text-m-slate-6 h-[1.5rem]", ), rx.el.span( con, class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[525]", ), - class_name="flex flex-row items-center gap-2.5", + class_name="flex flex-row items-start gap-2.5", ) for con in cons ], @@ -65,7 +65,7 @@ def pros_cons_cards(pros: list[str], cons: list[str]) -> rx.Component: def top_title(title: str) -> rx.Component: return rx.el.span( title, - class_name="text-m-slate-12 dark:text-m-slate-3 text-xs leading-[1.5rem] font-medium font-mono border-r border-m-slate-4 dark:border-m-slate-10 lg:px-12 lg:py-3 p-6 bg-secondary-1 dark:bg-m-slate-10 border-t", + class_name="text-m-slate-12 dark:text-m-slate-3 text-xs leading-[1.5rem] font-medium font-mono border-r border-m-slate-4 dark:border-m-slate-10 lg:px-8 lg:py-3 p-6 bg-secondary-1 dark:bg-m-slate-10 border-t", ) @@ -73,59 +73,59 @@ def comparison_cards() -> rx.Component: return rx.el.div( rx.el.div( top_title("Reflex"), - top_title("Bubble, Retool, Webflow, etc."), + top_title("Retool, Streamlit, Plotly Dash, Power BI"), class_name="grid grid-cols-2", ), comparison_title("Full Control Without the Ceiling", "CodesandboxIcon"), pros_cons_cards( [ - "Full control over your code", - "No limits on complexity", - "Customizable templates", + "Write real Python — no ceiling on what you can build", + "Handle custom logic, complex data flows, and performance optimization natively", + "Build anything a full-stack app can do", ], [ - "Limited control over your code", - "No limits on complexity", - "Customizable templates", + "Get you to v1 fast, then you hit walls", + "Custom logic and complex data flows require ugly workarounds", + "Platform limitations dictate what's possible, not your requirements", ], ), - comparison_title("Your own code", "SourceCodeSquareIcon"), + comparison_title("You Own Your Code", "SourceCodeSquareIcon"), pros_cons_cards( [ - "Full control over your code", - "No limits on complexity", - "Customizable templates", + "It's your Python code — deploy it anywhere", + "Full version control with Git", + "Never hostage to a platform's pricing or shutdown", ], [ - "Limited control over your code", - "No limits on complexity", - "Customizable templates", + "Your app lives on their infrastructure in their proprietary format", + "Vendor lock-in makes migration painful or impossible", + "Pricing changes or platform shutdowns put your app at risk", ], ), comparison_title("Python Ecosystem Access", "PythonIcon"), pros_cons_cards( [ - "Full control over your code", - "No limits on complexity", - "Customizable templates", + "Use libraries you already know — pandas, scikit-learn, whatever", + "Build internal tools or customer-facing apps in one language", + "Leverage the entire Python ecosystem with no restrictions", ], [ - "Limited control over your code", - "No limits on complexity", - "Customizable templates", + "Limited to the platform's pre-built integrations", + "Can't tap into Python's ML, data science, or backend libraries", + "Forces non-JS developers to learn new tools or work around limitations", ], ), comparison_title("Scales With Complexity", "SquareArrowExpand02Icon"), pros_cons_cards( [ - "Full control over your code", - "No limits on complexity", - "Customizable templates", + "Auth flows, real-time features, complex state management — all native", + "Handles growing complexity because it's just code", + "No artificial boundaries on what you can build", ], [ - "Limited control over your code", - "No limits on complexity", - "Customizable templates", + "Great for simple CRUD apps and dashboards, then it breaks down", + "Once you need real complexity, you're fighting the tool instead of building", + "Workarounds pile up and become unmaintainable", ], ), comparison_title( @@ -133,14 +133,14 @@ def comparison_cards() -> rx.Component: ), pros_cons_cards( [ - "Full control over your code", - "No limits on complexity", - "Customizable templates", + "Fits into normal engineering workflows — Git, PRs, CI/CD", + "Code review and automated testing work out of the box", + "Your whole team can collaborate using standard dev practices", ], [ - "Limited control over your code", - "No limits on complexity", - "Customizable templates", + "Version control is difficult or impossible", + "Code review and testing are afterthoughts at best", + "Engineering best practices don't apply to proprietary drag-and-drop formats", ], ), rx.el.div( @@ -170,13 +170,13 @@ def compare() -> rx.Component: rx.el.h1( "How You Benefit ", rx.el.br(class_name="max-lg:hidden"), - " With Reflex to ", + "With Reflex vs. ", rx.el.br(class_name="max-lg:hidden"), - " Other Approaches", + "Other Approaches", class_name="text-m-slate-12 dark:text-m-slate-3 text-3xl font-[575]", ), rx.el.h2( - "Reflex is growing-and we're looking for people who care deeply about developer experience, clean abstractions, and shipping things that matter.", + "No-code tools get you started fast, but Reflex lets you finish. Here's how Reflex compares to platforms like Retool, Streamlit, Plotly Dash, and Power BI.", class_name="text-m-slate-7 dark:text-m-slate-6 text-base font-[475]", ), class_name="flex flex-col gap-6 lg:max-w-[18rem] lg:sticky lg:top-[11rem] lg:self-start max-lg:self-center max-lg:items-center max-lg:text-center", diff --git a/pcweb/pages/migration/low_code/views/quotes.py b/pcweb/pages/migration/low_code/views/quotes.py index ed7d471d2..d6aa17bc5 100644 --- a/pcweb/pages/migration/low_code/views/quotes.py +++ b/pcweb/pages/migration/low_code/views/quotes.py @@ -28,22 +28,22 @@ class CompanyInfo(TypedDict): "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", }, Companies.FASTLY: { - "name": "Alex Atallah", - "title": "Co-founder & CEO, OpenSea", - "image": "/landing/social/alex_opensea.webp", - "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", + "name": "Emanuele Bonura", + "title": "Senior SOC Engineer", + "image": "", + "quote": "Migrating our cybersecurity app from Streamlit to Reflex has been excellent. We quickly built a unified interface connecting BigQuery, Salesforce, and PagerDuty for our 15+ team members. The ease of use and rapid development, supported by your responsive team, made it a great experience.", }, Companies.AUTODESK: { - "name": "Alex Atallah", - "title": "Co-founder & CEO, OpenSea", - "image": "/landing/social/alex_opensea.webp", - "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", + "name": "Paolo", + "title": "Principal Consultant", + "image": "", + "quote": "One person can do the job of two with Reflex, so it cut our cost in half. I am able to wear all the caps at once: Solution Architecture, UI/UX, front-end and back-end.", }, Companies.ACCENTURE: { - "name": "Alex Atallah", - "title": "Co-founder & CEO, OpenSea", - "image": "/landing/social/alex_opensea.webp", - "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", + "name": "Jordan Lee", + "title": "Senior Automation Developer", + "image": "", + "quote": "Reflex let us automate workflows that were impossible with previous low-code platforms. We went from prototype to rollout in days, and our team loves writing real Python instead of fighting drag-and-drop UI pain.", }, } From e1ab6fb4c7baf7e0c166c16061b91ddfffd61030 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Thu, 26 Feb 2026 13:39:49 +0100 Subject: [PATCH 4/6] updates --- assets/migration/dark/chatgpt.svg | 1 + assets/migration/dark/claude.svg | 1 + assets/migration/dark/django.svg | 1 + assets/migration/dark/fastapi.svg | 1 + assets/migration/dark/js.svg | 1 + assets/migration/dark/powerbi.svg | 10 +- assets/migration/dark/react.svg | 1 + assets/migration/dark/replit.svg | 1 + assets/migration/light/chatgpt.svg | 1 + assets/migration/light/claude.svg | 1 + assets/migration/light/django.svg | 1 + assets/migration/light/fastapi.svg | 1 + assets/migration/light/js.svg | 1 + assets/migration/light/powerbi.svg | 10 +- assets/migration/light/react.svg | 1 + assets/migration/light/replit.svg | 1 + pcweb/pages/migration/low_code/__init__.py | 4 +- .../migration/low_code/views/__init__.py | 3 +- .../pages/migration/low_code/views/explore.py | 101 ++++++++++++++++++ 19 files changed, 122 insertions(+), 20 deletions(-) create mode 100644 assets/migration/dark/chatgpt.svg create mode 100644 assets/migration/dark/claude.svg create mode 100644 assets/migration/dark/django.svg create mode 100644 assets/migration/dark/fastapi.svg create mode 100644 assets/migration/dark/js.svg create mode 100644 assets/migration/dark/react.svg create mode 100644 assets/migration/dark/replit.svg create mode 100644 assets/migration/light/chatgpt.svg create mode 100644 assets/migration/light/claude.svg create mode 100644 assets/migration/light/django.svg create mode 100644 assets/migration/light/fastapi.svg create mode 100644 assets/migration/light/js.svg create mode 100644 assets/migration/light/react.svg create mode 100644 assets/migration/light/replit.svg create mode 100644 pcweb/pages/migration/low_code/views/explore.py diff --git a/assets/migration/dark/chatgpt.svg b/assets/migration/dark/chatgpt.svg new file mode 100644 index 000000000..4268c44de --- /dev/null +++ b/assets/migration/dark/chatgpt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/claude.svg b/assets/migration/dark/claude.svg new file mode 100644 index 000000000..fcaafae22 --- /dev/null +++ b/assets/migration/dark/claude.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/django.svg b/assets/migration/dark/django.svg new file mode 100644 index 000000000..30291aa3c --- /dev/null +++ b/assets/migration/dark/django.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/fastapi.svg b/assets/migration/dark/fastapi.svg new file mode 100644 index 000000000..dd0d97fdf --- /dev/null +++ b/assets/migration/dark/fastapi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/js.svg b/assets/migration/dark/js.svg new file mode 100644 index 000000000..d15880a10 --- /dev/null +++ b/assets/migration/dark/js.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/powerbi.svg b/assets/migration/dark/powerbi.svg index 9433ff6e8..469973a41 100644 --- a/assets/migration/dark/powerbi.svg +++ b/assets/migration/dark/powerbi.svg @@ -1,9 +1 @@ - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/assets/migration/dark/react.svg b/assets/migration/dark/react.svg new file mode 100644 index 000000000..f1306c029 --- /dev/null +++ b/assets/migration/dark/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/dark/replit.svg b/assets/migration/dark/replit.svg new file mode 100644 index 000000000..cc76ee631 --- /dev/null +++ b/assets/migration/dark/replit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/chatgpt.svg b/assets/migration/light/chatgpt.svg new file mode 100644 index 000000000..4268c44de --- /dev/null +++ b/assets/migration/light/chatgpt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/claude.svg b/assets/migration/light/claude.svg new file mode 100644 index 000000000..fcaafae22 --- /dev/null +++ b/assets/migration/light/claude.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/django.svg b/assets/migration/light/django.svg new file mode 100644 index 000000000..30291aa3c --- /dev/null +++ b/assets/migration/light/django.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/fastapi.svg b/assets/migration/light/fastapi.svg new file mode 100644 index 000000000..dd0d97fdf --- /dev/null +++ b/assets/migration/light/fastapi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/js.svg b/assets/migration/light/js.svg new file mode 100644 index 000000000..d15880a10 --- /dev/null +++ b/assets/migration/light/js.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/powerbi.svg b/assets/migration/light/powerbi.svg index 9433ff6e8..469973a41 100644 --- a/assets/migration/light/powerbi.svg +++ b/assets/migration/light/powerbi.svg @@ -1,9 +1 @@ - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/assets/migration/light/react.svg b/assets/migration/light/react.svg new file mode 100644 index 000000000..f1306c029 --- /dev/null +++ b/assets/migration/light/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/migration/light/replit.svg b/assets/migration/light/replit.svg new file mode 100644 index 000000000..cc76ee631 --- /dev/null +++ b/assets/migration/light/replit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pcweb/pages/migration/low_code/__init__.py b/pcweb/pages/migration/low_code/__init__.py index 60a67bb14..1e549fb39 100644 --- a/pcweb/pages/migration/low_code/__init__.py +++ b/pcweb/pages/migration/low_code/__init__.py @@ -5,7 +5,7 @@ from pcweb.meta.meta import create_meta_tags from pcweb.pages.about.views.divider import divider from pcweb.pages.framework.views.footer_index import footer_index -from pcweb.pages.migration.low_code.views import compare, hero, quotes +from pcweb.pages.migration.low_code.views import compare, explore, hero, quotes from pcweb.views.marketing_navbar import marketing_navbar @@ -28,6 +28,8 @@ def low_code_migration_page() -> rx.Component: divider(), compare(), divider(), + explore(), + divider(), footer_index(), class_name="flex flex-col relative justify-center items-center w-full", ), diff --git a/pcweb/pages/migration/low_code/views/__init__.py b/pcweb/pages/migration/low_code/views/__init__.py index 7ad7dc9ad..f76295d0f 100644 --- a/pcweb/pages/migration/low_code/views/__init__.py +++ b/pcweb/pages/migration/low_code/views/__init__.py @@ -1,5 +1,6 @@ from .compare import compare +from .explore import explore from .hero import hero from .quotes import quotes -__all__ = ["compare", "hero", "quotes"] +__all__ = ["compare", "explore", "hero", "quotes"] diff --git a/pcweb/pages/migration/low_code/views/explore.py b/pcweb/pages/migration/low_code/views/explore.py new file mode 100644 index 000000000..8bbe32d28 --- /dev/null +++ b/pcweb/pages/migration/low_code/views/explore.py @@ -0,0 +1,101 @@ +from typing import TypedDict + +import reflex as rx +import reflex_ui as ui + + +class Feature(TypedDict): + title: str + description: str + icon: str + + +FEATURES: list[Feature] = [ + { + "title": "AI-Powered Development", + "description": "Build your app with the speed of AI and precision of Python", + "icon": "ArtificialIntelligence04Icon", + }, + { + "title": "Python-Powered Development", + "description": "Build your app with the speed of AI and precision of Python", + "icon": "RepeatIcon", + }, + { + "title": "Python-Powered Development", + "description": "Build your app with the speed of AI and precision of Python", + "icon": "FlowConnectionIcon", + }, + { + "title": "Python-Powered Development", + "description": "Build your app with the speed of AI and precision of Python", + "icon": "Layers01Icon", + }, + { + "title": "Python-Powered Development", + "description": "Build your app with the speed of AI and precision of Python", + "icon": "Layers01Icon", + }, + { + "title": "Python-Powered Development", + "description": "Build your app with the speed of AI and precision of Python", + "icon": "Layers01Icon", + }, +] + + +def feature_card(feature: Feature) -> rx.Component: + return rx.el.div( + rx.el.div( + ui.icon( + feature["icon"], class_name="text-primary-10 dark:text-primary-9 size-5" + ), + rx.el.span( + feature["title"], + class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525]", + ), + class_name="flex flex-row gap-2.5 items-center", + ), + rx.el.p( + feature["description"], + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475] text-start", + ), + class_name="flex flex-col gap-2 justify-start", + ) + + +def feature_grid() -> rx.Component: + return rx.el.div( + *[feature_card(feature) for feature in FEATURES], + class_name="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-12 gap-y-16", + ) + + +def explore() -> rx.Component: + return rx.el.section( + rx.el.div( + rx.el.div( + rx.el.p( + "Explore", + class_name="text-sm font-[525] text-primary-10 max-lg:text-center dark:text-m-slate-6", + ), + rx.el.div( + rx.el.h1( + "Build Your App With the Speed of ", + rx.el.br(), + "AI and Precision of Python", + class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-4xl text-3xl font-[575] shrink-0", + ), + rx.el.p( + "Reflex is growing—and we’re looking for people who care deeply about developer experience, clean abstractions.", + class_name="text-base text-m-slate-7 dark:text-m-slate-6 font-[475]", + ), + class_name="flex lg:flex-row flex-col gap-8 lg:gap-36", + ), + class_name="relative flex flex-col gap-6 lg:py-24 py-16", + ), + feature_grid(), + class_name="flex flex-col gap-16 max-w-(--layout-max-width) mx-auto lg:px-24 px-6 max-lg:text-center relative lg:pb-24 pb-16", + ), + 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", + ) From 69a89927a570dd482480ee7b1131a1c7be013927 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Thu, 26 Feb 2026 18:12:29 +0100 Subject: [PATCH 5/6] template --- pcweb/pages/migration/common/compare.py | 154 +++++++++++ pcweb/pages/migration/common/explore.py | 74 +++++ pcweb/pages/migration/common/hero.py | 101 +++++++ pcweb/pages/migration/common/quotes.py | 113 ++++++++ .../pages/migration/low_code/views/compare.py | 261 ++++++------------ .../pages/migration/low_code/views/explore.py | 107 ++----- pcweb/pages/migration/low_code/views/hero.py | 131 +++------ .../pages/migration/low_code/views/quotes.py | 151 +++------- 8 files changed, 614 insertions(+), 478 deletions(-) create mode 100644 pcweb/pages/migration/common/compare.py create mode 100644 pcweb/pages/migration/common/explore.py create mode 100644 pcweb/pages/migration/common/hero.py create mode 100644 pcweb/pages/migration/common/quotes.py diff --git a/pcweb/pages/migration/common/compare.py b/pcweb/pages/migration/common/compare.py new file mode 100644 index 000000000..cf4bb8177 --- /dev/null +++ b/pcweb/pages/migration/common/compare.py @@ -0,0 +1,154 @@ +from typing import TypedDict + +import reflex as rx +import reflex_ui as ui + + +class ComparisonItem(TypedDict): + title: str + icon: str + pros: list[str] + cons: list[str] + + +def comparison_title(title: str, icon: str) -> rx.Component: + return rx.el.div( + ui.icon(icon, stroke_width=1.5, class_name="shrink-0 lg:size-7 size-6"), + rx.el.span( + title, + class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-lg text-base font-[575]", + ), + class_name="flex flex-row items-center gap-3 lg:p-12 p-6 border-y border-r border-m-slate-4 dark:border-m-slate-10", + ) + + +def pros_card(pros: list[str]) -> rx.Component: + return rx.el.ul( + *[ + rx.el.li( + ui.icon( + "Tick02Icon", + class_name="shrink-0 text-primary-9 dark:text-primary-10 h-[1.5rem]", + ), + rx.el.span( + pro, + class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525]", + ), + class_name="flex flex-row items-start gap-2.5", + ) + for pro in pros + ], + class_name="list-inside flex flex-col gap-2 lg:p-12 p-6 [box-shadow:0_0_0_1px_rgba(0,_0,_0,_0.12)_inset,_0_6px_12px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_4px_6px_0_rgba(0,_0,_0,_0.02)] dark:shadow-none rounded-xl bg-white-1 dark:bg-m-slate-11 w-full", + ) + + +def cons_card(cons: list[str]) -> rx.Component: + return rx.el.ul( + *[ + rx.el.li( + ui.icon( + "MultiplicationSignIcon", + stroke_width=1.5, + class_name="shrink-0 text-m-slate-7 dark:text-m-slate-6 h-[1.5rem]", + ), + rx.el.span( + con, + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[525]", + ), + class_name="flex flex-row items-start gap-2.5", + ) + for con in cons + ], + class_name="list-inside flex flex-col gap-2 lg:p-12 p-6 w-full lg:border-x border-l border-m-slate-4 dark:border-m-slate-10", + ) + + +def pros_cons_cards(pros: list[str], cons: list[str]) -> rx.Component: + return rx.el.div( + pros_card(pros), + cons_card(cons), + class_name="grid lg:grid-cols-2 grid-cols-1 max-lg:border-r", + ) + + +def top_title(title: str) -> rx.Component: + return rx.el.span( + title, + class_name="text-m-slate-12 dark:text-m-slate-3 text-xs leading-[1.5rem] font-medium font-mono border-r border-m-slate-4 dark:border-m-slate-10 lg:px-8 lg:py-3 p-6 bg-secondary-1 dark:bg-m-slate-10 border-t", + ) + + +def heading_with_breaks(lines: list[str]) -> list[rx.Component | str]: + heading_parts: list[rx.Component | str] = [] + for index, line in enumerate(lines): + heading_parts.append(line) + if index < len(lines) - 1: + heading_parts.append(rx.el.br(class_name="max-lg:hidden")) + return heading_parts + + +def comparison_cards( + top_left_title: str, + top_right_title: str, + comparison_items: list[ComparisonItem], +) -> rx.Component: + return rx.el.div( + rx.el.div( + top_title(top_left_title), + top_title(top_right_title), + class_name="grid grid-cols-2", + ), + *[ + rx.fragment( + comparison_title(item["title"], item["icon"]), + pros_cons_cards(item["pros"], item["cons"]), + ) + for item in comparison_items + ], + rx.el.div( + class_name="absolute -top-24 right-0 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( + class_name="absolute -top-24 -left-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( + class_name="absolute -bottom-24 right-0 w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10" + ), + rx.el.div( + class_name="absolute -bottom-24 -left-px w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10" + ), + class_name="flex flex-col w-full max-w-[45rem] ml-auto border-l border-m-slate-4 dark:border-m-slate-10 mt-18 border-b mb-24 relative", + ) + + +def compare( + *, + kicker: str, + heading_lines: list[str], + description: str, + top_left_title: str, + top_right_title: str, + comparison_items: list[ComparisonItem], +) -> rx.Component: + return rx.el.section( + rx.el.div( + rx.el.div( + rx.el.p( + kicker, + class_name="text-sm font-[525] text-primary-10 max-lg:text-center dark:text-m-slate-6", + ), + rx.el.h1( + *heading_with_breaks(heading_lines), + class_name="text-m-slate-12 dark:text-m-slate-3 text-3xl font-[575]", + ), + rx.el.h2( + description, + class_name="text-m-slate-7 dark:text-m-slate-6 text-base font-[475]", + ), + class_name="flex flex-col gap-6 lg:max-w-[18rem] lg:sticky lg:top-[11rem] lg:self-start max-lg:self-center max-lg:items-center max-lg:text-center", + ), + comparison_cards(top_left_title, top_right_title, comparison_items), + class_name="flex lg:flex-row flex-col max-lg:gap-6 max-w-(--docs-layout-max-width) mx-auto relative py-24 max-lg:px-6", + ), + 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 relative", + ) diff --git a/pcweb/pages/migration/common/explore.py b/pcweb/pages/migration/common/explore.py new file mode 100644 index 000000000..716e81b62 --- /dev/null +++ b/pcweb/pages/migration/common/explore.py @@ -0,0 +1,74 @@ +from typing import TypedDict + +import reflex as rx +import reflex_ui as ui + + +class Feature(TypedDict): + title: str + description: str + icon: str + + +def feature_card(feature: Feature) -> rx.Component: + return rx.el.div( + rx.el.div( + ui.icon( + feature["icon"], class_name="text-primary-10 dark:text-primary-9 size-5" + ), + rx.el.span( + feature["title"], + class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525]", + ), + class_name="flex flex-row gap-2.5 items-center", + ), + rx.el.p( + feature["description"], + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475] text-start", + ), + class_name="flex flex-col gap-2 justify-start", + ) + + +def feature_grid(features: list[Feature]) -> rx.Component: + return rx.el.div( + *[feature_card(feature) for feature in features], + class_name="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-12 gap-y-16", + ) + + +def explore( + *, + kicker: str, + title_prefix: str, + title_suffix: str, + description: str, + features: list[Feature], +) -> rx.Component: + return rx.el.section( + rx.el.div( + rx.el.div( + rx.el.p( + kicker, + class_name="text-sm font-[525] text-primary-10 max-lg:text-center dark:text-m-slate-6", + ), + rx.el.div( + rx.el.h1( + title_prefix, + rx.el.br(), + title_suffix, + class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-4xl text-3xl font-[575] shrink-0", + ), + rx.el.p( + description, + class_name="text-base text-m-slate-7 dark:text-m-slate-6 font-[475]", + ), + class_name="flex lg:flex-row flex-col gap-8 lg:gap-36", + ), + class_name="relative flex flex-col gap-6 lg:py-24 py-16", + ), + feature_grid(features), + class_name="flex flex-col gap-16 max-w-(--layout-max-width) mx-auto lg:px-24 px-6 max-lg:text-center relative lg:pb-24 pb-16", + ), + 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", + ) diff --git a/pcweb/pages/migration/common/hero.py b/pcweb/pages/migration/common/hero.py new file mode 100644 index 000000000..117f33f09 --- /dev/null +++ b/pcweb/pages/migration/common/hero.py @@ -0,0 +1,101 @@ +from typing import TypedDict + +import reflex as rx +import reflex_ui as ui +from reflex_ui.blocks.demo_form import demo_form_dialog + +from pcweb.components.marketing_button import button + + +class HeroLogo(TypedDict): + image_name: str + alt: str + class_name: str + + +def floating_logo(logo: HeroLogo, logo_base_path: str) -> rx.Component: + return rx.el.div( + rx.image( + src=f"{logo_base_path}/{rx.color_mode_cond('light', 'dark')}/{logo['image_name']}", + alt=logo["alt"], + loading="eager", + custom_attrs={"fetchPriority": "high"}, + ), + class_name=ui.cn( + logo["class_name"], + "absolute left-1/2 -translate-x-1/2 -translate-y-1/2 z-[-1] pointer-events-none size-16 rounded-[1rem] bg-m-slate-1 dark:bg-m-slate-12 [box-shadow:0_1px_0_0_#FFF_inset,_0_0_0_1px_rgba(0,_0,_0,_0.12),_0_8px_16px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_4px_8px_0_rgba(0,_0,_0,_0.02)] dark:shadow-none dark:border dark:border-m-slate-9 flex items-center justify-center", + ), + ) + + +def gradient_logo() -> rx.Component: + return rx.el.div( + rx.image( + src=f"/logos/{rx.color_mode_cond('light', 'dark')}/gradient_r.svg", + alt="Gradient Reflex Logo", + loading="eager", + custom_attrs={"fetchPriority": "high"}, + ), + class_name="size-24 rounded-[1rem] bg-gradient-to-b from-m-slate-2 to-white-1 dark:from-m-slate-11 dark:to-m-slate-12 [box-shadow:0_1px_0_0_#FFF_inset,_0_0_0_1px_rgba(0,_0,_0,_0.12),_0_8px_16px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_4px_8px_0_rgba(0,_0,_0,_0.02)] dark:shadow-none dark:border dark:border-m-slate-9 flex items-center justify-center absolute left-1/2 -translate-x-1/2 -translate-y-1/2 z-[1] pointer-events-none top-[13.5rem]", + ) + + +def hero( + *, + kicker: str, + title: str, + subtitle: str, + cta_text: str, + logos: list[HeroLogo], + logo_base_path: str, +) -> rx.Component: + return rx.el.section( + rx.el.div( + rx.el.div( + rx.image( + src=f"/common/{rx.color_mode_cond('light', 'dark')}/grid.svg", + alt="Grid", + loading="eager", + custom_attrs={"fetchPriority": "high"}, + class_name=ui.cn( + "absolute left-1/2 -translate-x-1/2 z-[-1] pointer-events-none top-0", + ), + ), + *[floating_logo(logo, logo_base_path=logo_base_path) for logo in logos], + gradient_logo(), + class_name="max-lg:hidden", + ), + rx.el.p( + kicker, + class_name="text-sm font-[525] text-primary-10 dark:text-m-slate-6 -mt-6", + ), + rx.el.h1( + title, + class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-5xl text-3xl font-[575]", + ), + rx.el.h2( + subtitle, + class_name="text-m-slate-7 dark:text-m-slate-6 text-base font-[475]", + ), + demo_form_dialog( + trigger=button( + cta_text, + variant="primary", + size="lg", + native_button=False, + ), + ), + rx.el.div( + class_name="absolute -bottom-px -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10" + ), + rx.el.div( + class_name="absolute -bottom-px -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( + "flex flex-col gap-6 items-center justify-center text-center max-w-[45rem] pb-16 border-b border-m-slate-4 dark:border-m-slate-10 relative isolate lg:pt-[21.75rem] pt-[10.5rem]", + ), + ), + class_name=ui.cn( + "flex lg:flex-row flex-col max-w-(--layout-max-width) mx-auto lg:px-24 px-6 overflow-hidden", + ), + ) diff --git a/pcweb/pages/migration/common/quotes.py b/pcweb/pages/migration/common/quotes.py new file mode 100644 index 000000000..a59876981 --- /dev/null +++ b/pcweb/pages/migration/common/quotes.py @@ -0,0 +1,113 @@ +from typing import TypedDict + +import reflex as rx +import reflex_ui as ui +from reflex.experimental import ClientStateVar + + +class CompanyInfo(TypedDict): + key: str + logo_image_name: str + logo_alt: str + name: str + title: str + quote: str + profile_image: str + + +active_company_cs = ClientStateVar.create("active_company_cs", default="open_sea") + + +def company_card(company: CompanyInfo, logo_base_path: str) -> rx.Component: + return rx.el.button( + rx.image( + src=f"{logo_base_path}/{rx.color_mode_cond('light', 'dark')}/{company['logo_image_name']}", + alt=company["logo_alt"], + loading="lazy", + class_name=ui.cn( + "transition-[filter] group-hover:brightness-0 dark:group-hover:brightness-[10]", + rx.cond( + active_company_cs.value == company["key"], + "brightness-0 dark:brightness-[10]", + "", + ), + ), + ), + aria_label=f"Company: {company['key']}", + on_click=active_company_cs.set_value(company["key"]), + class_name=ui.cn( + "flex justify-end items-center h-12 py-3.5 lg:pr-12 pr-3.5 group", + rx.cond( + active_company_cs.value == company["key"], + "lg:shadow-[1px_0_0_0_var(--m-slate-12)] lg:dark:shadow-[1px_0_0_0_var(--m-slate-3)]", + "", + ), + ), + ) + + +def quote_card(company: CompanyInfo) -> rx.Component: + return rx.el.div( + rx.el.p( + company["quote"], + class_name="text-m-slate-12 dark:text-m-slate-3 text-lg font-[575] text-pretty", + ), + rx.el.div( + ui.gradient_profile( + seed=company["name"], + class_name="size-6 rounded-full", + ) + if not company["profile_image"] + else rx.image( + src=company["profile_image"], + loading="lazy", + alt=company["name"], + class_name="size-6 rounded-full", + ), + rx.el.span( + company["name"], + class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525]", + ), + rx.el.span( + company["title"], + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[525]", + ), + class_name="flex flex-row items-center gap-3", + ), + class_name="flex flex-col gap-8 lg:px-12 lg:pt-16 p-6 lg:w-[33rem] w-full", + ) + + +def companies_column(companies: list[CompanyInfo], logo_base_path: str) -> rx.Component: + return rx.el.div( + *[ + company_card(company, logo_base_path=logo_base_path) + for company in companies + ], + rx.el.div( + class_name="absolute -bottom-24 -right-px w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10 max-lg:hidden" + ), + class_name="flex max-lg:px-6 max-lg:overflow-x-auto lg:flex-col flex-row gap-2 pt-13 lg:max-w-[12rem] w-full lg:border-r border-m-slate-4 dark:border-m-slate-10 relative max-lg:justify-center", + ) + + +def quotes( + *, + companies: list[CompanyInfo], + default_active_key: str, + logo_base_path: str, +) -> rx.Component: + matches: list[tuple[str, rx.Component]] = [ + (company["key"], quote_card(company)) for company in companies + ] + + return rx.el.section( + companies_column(companies, logo_base_path=logo_base_path), + rx.match( + active_company_cs.value, + *matches, + quote_card(companies[0]), + ), + on_mount=active_company_cs.set_value(default_active_key), + class_name="flex lg:flex-row flex-col pb-24", + ) diff --git a/pcweb/pages/migration/low_code/views/compare.py b/pcweb/pages/migration/low_code/views/compare.py index c5c4e7caf..74fef35eb 100644 --- a/pcweb/pages/migration/low_code/views/compare.py +++ b/pcweb/pages/migration/low_code/views/compare.py @@ -1,188 +1,89 @@ -import reflex as rx -import reflex_ui as ui +from pcweb.pages.migration.common.compare import ComparisonItem +from pcweb.pages.migration.common.compare import compare as common_compare - -def comparison_title(title: str, icon: str) -> rx.Component: - return rx.el.div( - ui.icon(icon, stroke_width=1.5, class_name="shrink-0 lg:size-7 size-6"), - rx.el.span( - title, - class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-lg text-base font-[575]", - ), - class_name="flex flex-row items-center gap-3 lg:p-12 p-6 border-y border-r border-m-slate-4 dark:border-m-slate-10", - ) - - -def pros_card(pros: list[str]) -> rx.Component: - return rx.el.ul( - *[ - rx.el.li( - ui.icon( - "Tick02Icon", - class_name="shrink-0 text-primary-9 dark:text-primary-10 h-[1.5rem]", - ), - rx.el.span( - pro, - class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525]", - ), - class_name="flex flex-row items-start gap-2.5", - ) - for pro in pros +COMPARISON_ITEMS: list[ComparisonItem] = [ + { + "title": "Full Control Without the Ceiling", + "icon": "CodesandboxIcon", + "pros": [ + "Write real Python — no ceiling on what you can build", + "Handle custom logic, complex data flows, and performance optimization natively", + "Build anything a full-stack app can do", ], - class_name="list-inside flex flex-col gap-2 lg:p-12 p-6 [box-shadow:0_0_0_1px_rgba(0,_0,_0,_0.12)_inset,_0_6px_12px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_4px_6px_0_rgba(0,_0,_0,_0.02)] dark:shadow-none rounded-xl bg-white-1 dark:bg-m-slate-11 w-full", - ) - - -def cons_card(cons: list[str]) -> rx.Component: - return rx.el.ul( - *[ - rx.el.li( - ui.icon( - "MultiplicationSignIcon", - stroke_width=1.5, - class_name="shrink-0 text-m-slate-7 dark:text-m-slate-6 h-[1.5rem]", - ), - rx.el.span( - con, - class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[525]", - ), - class_name="flex flex-row items-start gap-2.5", - ) - for con in cons + "cons": [ + "Get you to v1 fast, then you hit walls", + "Custom logic and complex data flows require ugly workarounds", + "Platform limitations dictate what's possible, not your requirements", ], - class_name="list-inside flex flex-col gap-2 lg:p-12 p-6 w-full lg:border-x border-l border-m-slate-4 dark:border-m-slate-10", - ) - - -def pros_cons_cards(pros: list[str], cons: list[str]) -> rx.Component: - return rx.el.div( - pros_card(pros), - cons_card(cons), - class_name="grid lg:grid-cols-2 grid-cols-1 max-lg:border-r", - ) - - -def top_title(title: str) -> rx.Component: - return rx.el.span( - title, - class_name="text-m-slate-12 dark:text-m-slate-3 text-xs leading-[1.5rem] font-medium font-mono border-r border-m-slate-4 dark:border-m-slate-10 lg:px-8 lg:py-3 p-6 bg-secondary-1 dark:bg-m-slate-10 border-t", - ) + }, + { + "title": "You Own Your Code", + "icon": "SourceCodeSquareIcon", + "pros": [ + "It's your Python code — deploy it anywhere", + "Full version control with Git", + "Never hostage to a platform's pricing or shutdown", + ], + "cons": [ + "Your app lives on their infrastructure in their proprietary format", + "Vendor lock-in makes migration painful or impossible", + "Pricing changes or platform shutdowns put your app at risk", + ], + }, + { + "title": "Python Ecosystem Access", + "icon": "PythonIcon", + "pros": [ + "Use libraries you already know — pandas, scikit-learn, whatever", + "Build internal tools or customer-facing apps in one language", + "Leverage the entire Python ecosystem with no restrictions", + ], + "cons": [ + "Limited to the platform's pre-built integrations", + "Can't tap into Python's ML, data science, or backend libraries", + "Forces non-JS developers to learn new tools or work around limitations", + ], + }, + { + "title": "Scales With Complexity", + "icon": "SquareArrowExpand02Icon", + "pros": [ + "Auth flows, real-time features, complex state management — all native", + "Handles growing complexity because it's just code", + "No artificial boundaries on what you can build", + ], + "cons": [ + "Great for simple CRUD apps and dashboards, then it breaks down", + "Once you need real complexity, you're fighting the tool instead of building", + "Workarounds pile up and become unmaintainable", + ], + }, + { + "title": "Team Collaboration & Engineering Practices", + "icon": "UserSwitchIcon", + "pros": [ + "Fits into normal engineering workflows — Git, PRs, CI/CD", + "Code review and automated testing work out of the box", + "Your whole team can collaborate using standard dev practices", + ], + "cons": [ + "Version control is difficult or impossible", + "Code review and testing are afterthoughts at best", + "Engineering best practices don't apply to proprietary drag-and-drop formats", + ], + }, +] -def comparison_cards() -> rx.Component: - return rx.el.div( - rx.el.div( - top_title("Reflex"), - top_title("Retool, Streamlit, Plotly Dash, Power BI"), - class_name="grid grid-cols-2", - ), - comparison_title("Full Control Without the Ceiling", "CodesandboxIcon"), - pros_cons_cards( - [ - "Write real Python — no ceiling on what you can build", - "Handle custom logic, complex data flows, and performance optimization natively", - "Build anything a full-stack app can do", - ], - [ - "Get you to v1 fast, then you hit walls", - "Custom logic and complex data flows require ugly workarounds", - "Platform limitations dictate what's possible, not your requirements", - ], - ), - comparison_title("You Own Your Code", "SourceCodeSquareIcon"), - pros_cons_cards( - [ - "It's your Python code — deploy it anywhere", - "Full version control with Git", - "Never hostage to a platform's pricing or shutdown", - ], - [ - "Your app lives on their infrastructure in their proprietary format", - "Vendor lock-in makes migration painful or impossible", - "Pricing changes or platform shutdowns put your app at risk", - ], - ), - comparison_title("Python Ecosystem Access", "PythonIcon"), - pros_cons_cards( - [ - "Use libraries you already know — pandas, scikit-learn, whatever", - "Build internal tools or customer-facing apps in one language", - "Leverage the entire Python ecosystem with no restrictions", - ], - [ - "Limited to the platform's pre-built integrations", - "Can't tap into Python's ML, data science, or backend libraries", - "Forces non-JS developers to learn new tools or work around limitations", - ], - ), - comparison_title("Scales With Complexity", "SquareArrowExpand02Icon"), - pros_cons_cards( - [ - "Auth flows, real-time features, complex state management — all native", - "Handles growing complexity because it's just code", - "No artificial boundaries on what you can build", - ], - [ - "Great for simple CRUD apps and dashboards, then it breaks down", - "Once you need real complexity, you're fighting the tool instead of building", - "Workarounds pile up and become unmaintainable", - ], - ), - comparison_title( - "Team Collaboration & Engineering Practices", "UserSwitchIcon" - ), - pros_cons_cards( - [ - "Fits into normal engineering workflows — Git, PRs, CI/CD", - "Code review and automated testing work out of the box", - "Your whole team can collaborate using standard dev practices", - ], - [ - "Version control is difficult or impossible", - "Code review and testing are afterthoughts at best", - "Engineering best practices don't apply to proprietary drag-and-drop formats", - ], - ), - rx.el.div( - class_name="absolute -top-24 right-0 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( - class_name="absolute -top-24 -left-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( - class_name="absolute -bottom-24 right-0 w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10" - ), - rx.el.div( - class_name="absolute -bottom-24 -left-px w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10" - ), - class_name="flex flex-col w-full max-w-[45rem] ml-auto border-l border-m-slate-4 dark:border-m-slate-10 mt-18 border-b mb-24 relative", +def compare(): + return common_compare( + kicker="Compare", + heading_lines=["How You Benefit ", "With Reflex vs. ", "Other Approaches"], + description="No-code tools get you started fast, but Reflex lets you finish. Here's how Reflex compares to platforms like Retool, Streamlit, Plotly Dash, and Power BI.", + top_left_title="Reflex", + top_right_title="Retool, Streamlit, Plotly Dash, Power BI", + comparison_items=COMPARISON_ITEMS, ) -def compare() -> rx.Component: - return rx.el.section( - rx.el.div( - rx.el.div( - rx.el.p( - "Compare", - class_name="text-sm font-[525] text-primary-10 max-lg:text-center dark:text-m-slate-6", - ), - rx.el.h1( - "How You Benefit ", - rx.el.br(class_name="max-lg:hidden"), - "With Reflex vs. ", - rx.el.br(class_name="max-lg:hidden"), - "Other Approaches", - class_name="text-m-slate-12 dark:text-m-slate-3 text-3xl font-[575]", - ), - rx.el.h2( - "No-code tools get you started fast, but Reflex lets you finish. Here's how Reflex compares to platforms like Retool, Streamlit, Plotly Dash, and Power BI.", - class_name="text-m-slate-7 dark:text-m-slate-6 text-base font-[475]", - ), - class_name="flex flex-col gap-6 lg:max-w-[18rem] lg:sticky lg:top-[11rem] lg:self-start max-lg:self-center max-lg:items-center max-lg:text-center", - ), - comparison_cards(), - class_name="flex lg:flex-row flex-col max-lg:gap-6 max-w-(--docs-layout-max-width) mx-auto relative py-24 max-lg:px-6", - ), - 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 relative", - ) +__all__ = ["compare"] diff --git a/pcweb/pages/migration/low_code/views/explore.py b/pcweb/pages/migration/low_code/views/explore.py index 8bbe32d28..18ca500f7 100644 --- a/pcweb/pages/migration/low_code/views/explore.py +++ b/pcweb/pages/migration/low_code/views/explore.py @@ -1,101 +1,48 @@ -from typing import TypedDict - -import reflex as rx -import reflex_ui as ui - - -class Feature(TypedDict): - title: str - description: str - icon: str - +from pcweb.pages.migration.common.explore import Feature +from pcweb.pages.migration.common.explore import explore as common_explore FEATURES: list[Feature] = [ { - "title": "AI-Powered Development", - "description": "Build your app with the speed of AI and precision of Python", + "title": "AI-Assisted App Development", + "description": "Generate UI and app logic faster with AI while keeping full code control.", "icon": "ArtificialIntelligence04Icon", }, { - "title": "Python-Powered Development", - "description": "Build your app with the speed of AI and precision of Python", - "icon": "RepeatIcon", + "title": "Real Python, Not Drag-and-Drop", + "description": "Write maintainable Python code that fits your existing engineering standards.", + "icon": "SourceCodeSquareIcon", }, { - "title": "Python-Powered Development", - "description": "Build your app with the speed of AI and precision of Python", - "icon": "FlowConnectionIcon", + "title": "Data Stack Integrations", + "description": "Connect databases, APIs, and internal services without low-code workarounds.", + "icon": "DatabaseIcon", }, { - "title": "Python-Powered Development", - "description": "Build your app with the speed of AI and precision of Python", - "icon": "Layers01Icon", + "title": "Security-First by Design", + "description": "Deploy with full control over infrastructure, access, and compliance boundaries.", + "icon": "ShieldEnergyIcon", }, { - "title": "Python-Powered Development", - "description": "Build your app with the speed of AI and precision of Python", - "icon": "Layers01Icon", + "title": "Production-Grade Performance", + "description": "Scale from internal tools to enterprise apps with robust backend capabilities.", + "icon": "DashboardSpeed01Icon", }, { - "title": "Python-Powered Development", - "description": "Build your app with the speed of AI and precision of Python", - "icon": "Layers01Icon", + "title": "Team-Friendly Workflows", + "description": "Use Git, PR reviews, and CI/CD workflows your team already trusts.", + "icon": "UserSwitchIcon", }, ] -def feature_card(feature: Feature) -> rx.Component: - return rx.el.div( - rx.el.div( - ui.icon( - feature["icon"], class_name="text-primary-10 dark:text-primary-9 size-5" - ), - rx.el.span( - feature["title"], - class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525]", - ), - class_name="flex flex-row gap-2.5 items-center", - ), - rx.el.p( - feature["description"], - class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475] text-start", - ), - class_name="flex flex-col gap-2 justify-start", +def explore(): + return common_explore( + kicker="Explore", + title_prefix="Build Your App With the Speed of ", + title_suffix="AI and Precision of Python", + description="Reflex is growing—and we're looking for people who care deeply about developer experience, clean abstractions.", + features=FEATURES, ) -def feature_grid() -> rx.Component: - return rx.el.div( - *[feature_card(feature) for feature in FEATURES], - class_name="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-12 gap-y-16", - ) - - -def explore() -> rx.Component: - return rx.el.section( - rx.el.div( - rx.el.div( - rx.el.p( - "Explore", - class_name="text-sm font-[525] text-primary-10 max-lg:text-center dark:text-m-slate-6", - ), - rx.el.div( - rx.el.h1( - "Build Your App With the Speed of ", - rx.el.br(), - "AI and Precision of Python", - class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-4xl text-3xl font-[575] shrink-0", - ), - rx.el.p( - "Reflex is growing—and we’re looking for people who care deeply about developer experience, clean abstractions.", - class_name="text-base text-m-slate-7 dark:text-m-slate-6 font-[475]", - ), - class_name="flex lg:flex-row flex-col gap-8 lg:gap-36", - ), - class_name="relative flex flex-col gap-6 lg:py-24 py-16", - ), - feature_grid(), - class_name="flex flex-col gap-16 max-w-(--layout-max-width) mx-auto lg:px-24 px-6 max-lg:text-center relative lg:pb-24 pb-16", - ), - 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", - ) +__all__ = ["explore"] diff --git a/pcweb/pages/migration/low_code/views/hero.py b/pcweb/pages/migration/low_code/views/hero.py index 0f03410a4..8034f27a8 100644 --- a/pcweb/pages/migration/low_code/views/hero.py +++ b/pcweb/pages/migration/low_code/views/hero.py @@ -1,104 +1,39 @@ -import reflex as rx -import reflex_ui as ui -from reflex_ui.blocks.demo_form import demo_form_dialog +from pcweb.pages.migration.common.hero import HeroLogo +from pcweb.pages.migration.common.hero import hero as common_hero -from pcweb.components.marketing_button import button +HERO_LOGOS: list[HeroLogo] = [ + { + "image_name": "plotly.svg", + "alt": "Plotly Logo", + "class_name": "top-[9.5rem] -ml-[10.5rem]", + }, + { + "image_name": "powerbi.svg", + "alt": "Power BI Logo", + "class_name": "top-[15rem] ml-[16.5rem]", + }, + { + "image_name": "retool.svg", + "alt": "Retool Logo", + "class_name": "top-[9.5rem] ml-[10.5rem]", + }, + { + "image_name": "streamlit.svg", + "alt": "Streamlit Logo", + "class_name": "top-[15rem] -ml-[16.5rem]", + }, +] -def floating_logo(src: str, alt: str, class_name: str = "") -> rx.Component: - return rx.el.div( - rx.image( - src=src, - alt=alt, - loading="eager", - custom_attrs={"fetchPriority": "high"}, - ), - class_name=ui.cn( - class_name, - "absolute left-1/2 -translate-x-1/2 -translate-y-1/2 z-[-1] pointer-events-none size-16 rounded-[1rem] bg-m-slate-1 dark:bg-m-slate-12 [box-shadow:0_1px_0_0_#FFF_inset,_0_0_0_1px_rgba(0,_0,_0,_0.12),_0_8px_16px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_4px_8px_0_rgba(0,_0,_0,_0.02)] dark:shadow-none dark:border dark:border-m-slate-9 flex items-center justify-center", - ), +def hero(): + return common_hero( + kicker="Move From Low Code to Reflex", + title="The Next-Gen Platform Built for Modern Enterprises", + subtitle="Escape low-code constraints without sacrificing speed. Build production-grade apps in pure Python with complete control over your stack.", + cta_text="Book a Demo", + logos=HERO_LOGOS, + logo_base_path="/migration", ) -def gradient_logo() -> rx.Component: - return rx.el.div( - rx.image( - src=f"/logos/{rx.color_mode_cond('light', 'dark')}/gradient_r.svg", - alt="Gradient Reflex Logo", - loading="eager", - custom_attrs={"fetchPriority": "high"}, - ), - class_name="size-24 rounded-[1rem] bg-gradient-to-b from-m-slate-2 to-white-1 dark:from-m-slate-11 dark:to-m-slate-12 [box-shadow:0_1px_0_0_#FFF_inset,_0_0_0_1px_rgba(0,_0,_0,_0.12),_0_8px_16px_0_rgba(0,_0,_0,_0.06),_0_1px_1px_0_rgba(0,_0,_0,_0.01),_0_4px_8px_0_rgba(0,_0,_0,_0.02)] dark:shadow-none dark:border dark:border-m-slate-9 flex items-center justify-center absolute left-1/2 -translate-x-1/2 -translate-y-1/2 z-[1] pointer-events-none top-[13.5rem]", - ) - - -def hero() -> rx.Component: - return rx.el.section( - rx.el.div( - rx.el.div( - rx.image( - src=f"/common/{rx.color_mode_cond('light', 'dark')}/grid.svg", - alt="Grid", - loading="eager", - custom_attrs={"fetchPriority": "high"}, - class_name=ui.cn( - "absolute left-1/2 -translate-x-1/2 z-[-1] pointer-events-none top-[2.815rem]", - ), - ), - floating_logo( - src=f"/migration/{rx.color_mode_cond('light', 'dark')}/plotly.svg", - alt="Plotly Logo", - class_name="top-[9.5rem] -ml-[10.5rem]", - ), - floating_logo( - src=f"/migration/{rx.color_mode_cond('light', 'dark')}/powerbi.svg", - alt="Power BI Logo", - class_name="top-[15rem] ml-[16.5rem]", - ), - floating_logo( - src=f"/migration/{rx.color_mode_cond('light', 'dark')}/retool.svg", - alt="Retool Logo", - class_name="top-[9.5rem] ml-[10.5rem]", - ), - floating_logo( - src=f"/migration/{rx.color_mode_cond('light', 'dark')}/streamlit.svg", - alt="Streamlit Logo", - class_name="top-[15rem] -ml-[16.5rem]", - ), - gradient_logo(), - class_name="max-lg:hidden", - ), - rx.el.p( - "Move From Low Code to Reflex", - class_name="text-sm font-[525] text-primary-10 dark:text-m-slate-6", - ), - rx.el.h1( - "The Next-Gen Platform Built for Modern Enterprises", - class_name="text-m-slate-12 dark:text-m-slate-3 lg:text-5xl text-3xl font-[575]", - ), - rx.el.h2( - "Escape low-code constraints without sacrificing speed. Build production-grade apps in pure Python with complete control over your stack.", - class_name="text-m-slate-7 dark:text-m-slate-6 text-base font-[475]", - ), - demo_form_dialog( - trigger=button( - "Book a Demo", - variant="primary", - size="lg", - native_button=False, - ), - ), - rx.el.div( - class_name="absolute -bottom-px -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10" - ), - rx.el.div( - class_name="absolute -bottom-px -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( - "flex flex-col gap-6 items-center justify-center text-center max-w-[45rem] pb-16 border-b border-m-slate-4 dark:border-m-slate-10 relative isolate lg:pt-[21.75rem] pt-[10.5rem]", - ), - ), - class_name=ui.cn( - "flex lg:flex-row flex-col max-w-(--layout-max-width) mx-auto lg:px-24 px-6 overflow-hidden", - ), - ) +__all__ = ["hero"] diff --git a/pcweb/pages/migration/low_code/views/quotes.py b/pcweb/pages/migration/low_code/views/quotes.py index d6aa17bc5..04ea15888 100644 --- a/pcweb/pages/migration/low_code/views/quotes.py +++ b/pcweb/pages/migration/low_code/views/quotes.py @@ -1,141 +1,52 @@ -from enum import StrEnum -from typing import TypedDict - -import reflex as rx -import reflex_ui as ui -from reflex.experimental import ClientStateVar - - -class Companies(StrEnum): - OPEN_SEA = "open_sea" - FASTLY = "fastly" - AUTODESK = "autodesk" - ACCENTURE = "accenture" - - -class CompanyInfo(TypedDict): - name: str - title: str - quote: str - image: str - - -COMPANIES_INFO: dict[Companies, CompanyInfo] = { - Companies.OPEN_SEA: { +from pcweb.pages.migration.common.quotes import CompanyInfo +from pcweb.pages.migration.common.quotes import quotes as common_quotes + +COMPANIES: list[CompanyInfo] = [ + { + "key": "open_sea", + "logo_image_name": "open_sea.svg", + "logo_alt": "OpenSea logo", "name": "Alex Atallah", "title": "Co-founder & CEO, OpenSea", - "image": "/landing/social/alex_opensea.webp", "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", + "profile_image": "/landing/social/alex_opensea.webp", }, - Companies.FASTLY: { + { + "key": "fastly", + "logo_image_name": "fastly.svg", + "logo_alt": "Fastly logo", "name": "Emanuele Bonura", "title": "Senior SOC Engineer", - "image": "", "quote": "Migrating our cybersecurity app from Streamlit to Reflex has been excellent. We quickly built a unified interface connecting BigQuery, Salesforce, and PagerDuty for our 15+ team members. The ease of use and rapid development, supported by your responsive team, made it a great experience.", + "profile_image": "", }, - Companies.AUTODESK: { + { + "key": "autodesk", + "logo_image_name": "autodesk.svg", + "logo_alt": "Autodesk logo", "name": "Paolo", "title": "Principal Consultant", - "image": "", "quote": "One person can do the job of two with Reflex, so it cut our cost in half. I am able to wear all the caps at once: Solution Architecture, UI/UX, front-end and back-end.", + "profile_image": "", }, - Companies.ACCENTURE: { + { + "key": "accenture", + "logo_image_name": "accenture.svg", + "logo_alt": "Accenture logo", "name": "Jordan Lee", "title": "Senior Automation Developer", - "image": "", "quote": "Reflex let us automate workflows that were impossible with previous low-code platforms. We went from prototype to rollout in days, and our team loves writing real Python instead of fighting drag-and-drop UI pain.", + "profile_image": "", }, -} - +] -active_company_cs = ClientStateVar.create( - "active_company_cs", default=Companies.OPEN_SEA -) - -def company_card(company: Companies) -> rx.Component: - return rx.el.button( - rx.image( - src=f"/migration/{rx.color_mode_cond('light', 'dark')}/{company}.svg", - alt=f"{company} logo", - loading="lazy", - class_name=ui.cn( - "transition-[filter] group-hover:brightness-0 dark:group-hover:brightness-[10]", - rx.cond( - active_company_cs.value == company, - "brightness-0 dark:brightness-[10]", - "", - ), - ), - ), - aria_label=f"Company: {company}", - on_click=active_company_cs.set_value(company), - class_name=ui.cn( - "flex justify-end items-center h-12 py-3.5 lg:pr-12 pr-3.5 group", - rx.cond( - active_company_cs.value == company, - "lg:shadow-[1px_0_0_0_var(--m-slate-12)] lg:dark:shadow-[1px_0_0_0_var(--m-slate-3)]", - "", - ), - ), - ) - - -def quote_card(company: Companies) -> rx.Component: - return rx.el.div( - rx.el.p( - COMPANIES_INFO[company]["quote"], - class_name="text-m-slate-12 dark:text-m-slate-3 text-lg font-[575] text-pretty", - ), - rx.el.div( - ui.gradient_profile( - seed=COMPANIES_INFO[company]["name"], - class_name="size-6 rounded-full", - ) - if not COMPANIES_INFO[company]["image"] - else rx.image( - src=COMPANIES_INFO[company]["image"], - loading="lazy", - alt=f"{company} logo", - class_name="size-6 rounded-full", - ), - rx.el.span( - COMPANIES_INFO[company]["name"], - class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525]", - ), - rx.el.span( - COMPANIES_INFO[company]["title"], - class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[525]", - ), - class_name="flex flex-row items-center gap-3", - ), - class_name="flex flex-col gap-8 lg:px-12 lg:pt-16 p-6 lg:w-[33rem] w-full", +def quotes(): + return common_quotes( + companies=COMPANIES, + default_active_key="open_sea", + logo_base_path="/migration", ) -def companies_column() -> rx.Component: - return rx.el.div( - company_card(Companies.OPEN_SEA), - company_card(Companies.FASTLY), - company_card(Companies.AUTODESK), - company_card(Companies.ACCENTURE), - rx.el.div( - class_name="absolute -bottom-24 -right-px w-px h-24 bg-gradient-to-b from-current to-transparent text-m-slate-4 dark:text-m-slate-10 max-lg:hidden" - ), - class_name="flex max-lg:px-6 max-lg:overflow-x-auto lg:flex-col flex-row gap-2 pt-13 lg:max-w-[12rem] w-full lg:border-r border-m-slate-4 dark:border-m-slate-10 relative max-lg:justify-center", - ) - - -def quotes() -> rx.Component: - return rx.el.section( - companies_column(), - rx.match( - active_company_cs.value, - (Companies.OPEN_SEA, quote_card(Companies.OPEN_SEA)), - (Companies.FASTLY, quote_card(Companies.FASTLY)), - (Companies.AUTODESK, quote_card(Companies.AUTODESK)), - (Companies.ACCENTURE, quote_card(Companies.ACCENTURE)), - quote_card(Companies.OPEN_SEA), - ), - class_name="flex lg:flex-row flex-col pb-24", - ) +__all__ = ["quotes"] From d9bf609bac9dd2f7e87dabeeb4baaa144f29b854 Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Thu, 26 Feb 2026 20:10:13 +0100 Subject: [PATCH 6/6] add other pages --- assets/migration/dark/lovable.svg | 46 ++++++++++ assets/migration/dark/replit.svg | 6 +- assets/migration/light/lovable.svg | 46 ++++++++++ assets/migration/light/replit.svg | 6 +- pcweb/pages/__init__.py | 6 ++ .../migration/other_ai_tools/__init__.py | 46 ++++++++++ .../other_ai_tools/views/__init__.py | 6 ++ .../migration/other_ai_tools/views/compare.py | 89 +++++++++++++++++++ .../migration/other_ai_tools/views/explore.py | 48 ++++++++++ .../migration/other_ai_tools/views/hero.py | 39 ++++++++ .../migration/other_ai_tools/views/quotes.py | 52 +++++++++++ .../migration/other_frameworks/__init__.py | 46 ++++++++++ .../other_frameworks/views/__init__.py | 6 ++ .../other_frameworks/views/compare.py | 89 +++++++++++++++++++ .../other_frameworks/views/explore.py | 48 ++++++++++ .../migration/other_frameworks/views/hero.py | 39 ++++++++ .../other_frameworks/views/quotes.py | 52 +++++++++++ pcweb/views/marketing_navbar.py | 4 +- 18 files changed, 670 insertions(+), 4 deletions(-) create mode 100644 assets/migration/dark/lovable.svg create mode 100644 assets/migration/light/lovable.svg create mode 100644 pcweb/pages/migration/other_ai_tools/__init__.py create mode 100644 pcweb/pages/migration/other_ai_tools/views/__init__.py create mode 100644 pcweb/pages/migration/other_ai_tools/views/compare.py create mode 100644 pcweb/pages/migration/other_ai_tools/views/explore.py create mode 100644 pcweb/pages/migration/other_ai_tools/views/hero.py create mode 100644 pcweb/pages/migration/other_ai_tools/views/quotes.py create mode 100644 pcweb/pages/migration/other_frameworks/__init__.py create mode 100644 pcweb/pages/migration/other_frameworks/views/__init__.py create mode 100644 pcweb/pages/migration/other_frameworks/views/compare.py create mode 100644 pcweb/pages/migration/other_frameworks/views/explore.py create mode 100644 pcweb/pages/migration/other_frameworks/views/hero.py create mode 100644 pcweb/pages/migration/other_frameworks/views/quotes.py diff --git a/assets/migration/dark/lovable.svg b/assets/migration/dark/lovable.svg new file mode 100644 index 000000000..0c076f09f --- /dev/null +++ b/assets/migration/dark/lovable.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/migration/dark/replit.svg b/assets/migration/dark/replit.svg index cc76ee631..a4ee3f7ed 100644 --- a/assets/migration/dark/replit.svg +++ b/assets/migration/dark/replit.svg @@ -1 +1,5 @@ - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/assets/migration/light/lovable.svg b/assets/migration/light/lovable.svg new file mode 100644 index 000000000..0c076f09f --- /dev/null +++ b/assets/migration/light/lovable.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/migration/light/replit.svg b/assets/migration/light/replit.svg index cc76ee631..a4ee3f7ed 100644 --- a/assets/migration/light/replit.svg +++ b/assets/migration/light/replit.svg @@ -1 +1,5 @@ - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/pcweb/pages/__init__.py b/pcweb/pages/__init__.py index 6a4d58872..ad600843f 100644 --- a/pcweb/pages/__init__.py +++ b/pcweb/pages/__init__.py @@ -21,6 +21,12 @@ page_meeting_successfully_booked as page_meeting_successfully_booked, ) from .migration.low_code import low_code_migration_page as low_code_migration_page +from .migration.other_ai_tools import ( + other_ai_tools_migration_page as other_ai_tools_migration_page, +) +from .migration.other_frameworks import ( + other_frameworks_migration_page as other_frameworks_migration_page, +) from .page404 import page404 as page404 from .pricing.pricing import pricing as pricing from .sales import sales as sales diff --git a/pcweb/pages/migration/other_ai_tools/__init__.py b/pcweb/pages/migration/other_ai_tools/__init__.py new file mode 100644 index 000000000..925455ae4 --- /dev/null +++ b/pcweb/pages/migration/other_ai_tools/__init__.py @@ -0,0 +1,46 @@ +import reflex as rx +import reflex_ui as ui + +from pcweb.components.hosting_banner import HostingBannerState +from pcweb.meta.meta import create_meta_tags +from pcweb.pages.about.views.divider import divider +from pcweb.pages.framework.views.footer_index import footer_index +from pcweb.pages.migration.other_ai_tools.views import compare, explore, hero, quotes +from pcweb.views.marketing_navbar import marketing_navbar + + +@rx.page( + route="/migration/other-ai-tools", + title="Switch from Other AI Tools to Reflex", + meta=create_meta_tags( + title="Switch from Other AI Tools to Reflex", + description="Switch from Other AI Tools to Reflex - The platform to build and scale enterprise apps", + image="/previews/low_code_migration_preview.webp", + ), +) +def other_ai_tools_migration_page() -> rx.Component: + return rx.el.div( + marketing_navbar(), + rx.el.main( + rx.el.div( + hero(), + quotes(), + divider(), + compare(), + divider(), + explore(), + divider(), + footer_index(), + class_name="flex flex-col relative justify-center items-center w-full", + ), + class_name="flex flex-col w-full relative h-full justify-center items-center", + ), + class_name=ui.cn( + "flex flex-col w-full justify-center items-center relative dark:bg-m-slate-12 bg-m-slate-1", + rx.cond( + HostingBannerState.is_banner_visible, + "lg:pt-[7rem] pt-[3.5rem]", + "lg:pt-[4.5rem] pt-[3.5rem]", + ), + ), + ) diff --git a/pcweb/pages/migration/other_ai_tools/views/__init__.py b/pcweb/pages/migration/other_ai_tools/views/__init__.py new file mode 100644 index 000000000..f76295d0f --- /dev/null +++ b/pcweb/pages/migration/other_ai_tools/views/__init__.py @@ -0,0 +1,6 @@ +from .compare import compare +from .explore import explore +from .hero import hero +from .quotes import quotes + +__all__ = ["compare", "explore", "hero", "quotes"] diff --git a/pcweb/pages/migration/other_ai_tools/views/compare.py b/pcweb/pages/migration/other_ai_tools/views/compare.py new file mode 100644 index 000000000..14dc80e1e --- /dev/null +++ b/pcweb/pages/migration/other_ai_tools/views/compare.py @@ -0,0 +1,89 @@ +from pcweb.pages.migration.common.compare import ComparisonItem +from pcweb.pages.migration.common.compare import compare as common_compare + +COMPARISON_ITEMS: list[ComparisonItem] = [ + { + "title": "Full Control Without the Ceiling", + "icon": "CodesandboxIcon", + "pros": [ + "Write real Python — no ceiling on what you can build", + "Handle custom logic, complex data flows, and performance optimization natively", + "Build anything a full-stack app can do", + ], + "cons": [ + "Get you to v1 fast, then you hit walls", + "Custom logic and complex data flows require ugly workarounds", + "Platform limitations dictate what's possible, not your requirements", + ], + }, + { + "title": "You Own Your Code", + "icon": "SourceCodeSquareIcon", + "pros": [ + "It's your Python code — deploy it anywhere", + "Full version control with Git", + "Never hostage to a platform's pricing or shutdown", + ], + "cons": [ + "Your app lives on their infrastructure in their proprietary format", + "Vendor lock-in makes migration painful or impossible", + "Pricing changes or platform shutdowns put your app at risk", + ], + }, + { + "title": "Python Ecosystem Access", + "icon": "PythonIcon", + "pros": [ + "Use libraries you already know — pandas, scikit-learn, whatever", + "Build internal tools or customer-facing apps in one language", + "Leverage the entire Python ecosystem with no restrictions", + ], + "cons": [ + "Limited to the platform's pre-built integrations", + "Can't tap into Python's ML, data science, or backend libraries", + "Forces non-JS developers to learn new tools or work around limitations", + ], + }, + { + "title": "Scales With Complexity", + "icon": "SquareArrowExpand02Icon", + "pros": [ + "Auth flows, real-time features, complex state management — all native", + "Handles growing complexity because it's just code", + "No artificial boundaries on what you can build", + ], + "cons": [ + "Great for simple CRUD apps and dashboards, then it breaks down", + "Once you need real complexity, you're fighting the tool instead of building", + "Workarounds pile up and become unmaintainable", + ], + }, + { + "title": "Team Collaboration & Engineering Practices", + "icon": "UserSwitchIcon", + "pros": [ + "Fits into normal engineering workflows — Git, PRs, CI/CD", + "Code review and automated testing work out of the box", + "Your whole team can collaborate using standard dev practices", + ], + "cons": [ + "Version control is difficult or impossible", + "Code review and testing are afterthoughts at best", + "Engineering best practices don't apply to proprietary drag-and-drop formats", + ], + }, +] + + +def compare(): + return common_compare( + kicker="Compare", + heading_lines=["How You Benefit ", "With Reflex vs. ", "Other Approaches"], + description="AI tools get you started fast, but Reflex lets you finish. Here's how Reflex compares to platforms like ChatGPT, Claude, Replit, and Lovable.", + top_left_title="Reflex", + top_right_title="ChatGPT, Claude, Replit, Lovable", + comparison_items=COMPARISON_ITEMS, + ) + + +__all__ = ["compare"] diff --git a/pcweb/pages/migration/other_ai_tools/views/explore.py b/pcweb/pages/migration/other_ai_tools/views/explore.py new file mode 100644 index 000000000..18ca500f7 --- /dev/null +++ b/pcweb/pages/migration/other_ai_tools/views/explore.py @@ -0,0 +1,48 @@ +from pcweb.pages.migration.common.explore import Feature +from pcweb.pages.migration.common.explore import explore as common_explore + +FEATURES: list[Feature] = [ + { + "title": "AI-Assisted App Development", + "description": "Generate UI and app logic faster with AI while keeping full code control.", + "icon": "ArtificialIntelligence04Icon", + }, + { + "title": "Real Python, Not Drag-and-Drop", + "description": "Write maintainable Python code that fits your existing engineering standards.", + "icon": "SourceCodeSquareIcon", + }, + { + "title": "Data Stack Integrations", + "description": "Connect databases, APIs, and internal services without low-code workarounds.", + "icon": "DatabaseIcon", + }, + { + "title": "Security-First by Design", + "description": "Deploy with full control over infrastructure, access, and compliance boundaries.", + "icon": "ShieldEnergyIcon", + }, + { + "title": "Production-Grade Performance", + "description": "Scale from internal tools to enterprise apps with robust backend capabilities.", + "icon": "DashboardSpeed01Icon", + }, + { + "title": "Team-Friendly Workflows", + "description": "Use Git, PR reviews, and CI/CD workflows your team already trusts.", + "icon": "UserSwitchIcon", + }, +] + + +def explore(): + return common_explore( + kicker="Explore", + title_prefix="Build Your App With the Speed of ", + title_suffix="AI and Precision of Python", + description="Reflex is growing—and we're looking for people who care deeply about developer experience, clean abstractions.", + features=FEATURES, + ) + + +__all__ = ["explore"] diff --git a/pcweb/pages/migration/other_ai_tools/views/hero.py b/pcweb/pages/migration/other_ai_tools/views/hero.py new file mode 100644 index 000000000..854a66a34 --- /dev/null +++ b/pcweb/pages/migration/other_ai_tools/views/hero.py @@ -0,0 +1,39 @@ +from pcweb.pages.migration.common.hero import HeroLogo +from pcweb.pages.migration.common.hero import hero as common_hero + +HERO_LOGOS: list[HeroLogo] = [ + { + "image_name": "chatgpt.svg", + "alt": "ChatGPT Logo", + "class_name": "top-[9.5rem] -ml-[10.5rem]", + }, + { + "image_name": "claude.svg", + "alt": "Claude Logo", + "class_name": "top-[15rem] ml-[16.5rem]", + }, + { + "image_name": "replit.svg", + "alt": "Replit Logo", + "class_name": "top-[9.5rem] ml-[10.5rem]", + }, + { + "image_name": "lovable.svg", + "alt": "Lovable Logo", + "class_name": "top-[15rem] -ml-[16.5rem]", + }, +] + + +def hero(): + return common_hero( + kicker="Move From Other AI Tools to Reflex", + title="The Next-Gen Platform Built for Modern Enterprises", + subtitle="Escape AI tool constraints without sacrificing speed. Build production-grade apps in pure Python with complete control over your stack.", + cta_text="Book a Demo", + logos=HERO_LOGOS, + logo_base_path="/migration", + ) + + +__all__ = ["hero"] diff --git a/pcweb/pages/migration/other_ai_tools/views/quotes.py b/pcweb/pages/migration/other_ai_tools/views/quotes.py new file mode 100644 index 000000000..04ea15888 --- /dev/null +++ b/pcweb/pages/migration/other_ai_tools/views/quotes.py @@ -0,0 +1,52 @@ +from pcweb.pages.migration.common.quotes import CompanyInfo +from pcweb.pages.migration.common.quotes import quotes as common_quotes + +COMPANIES: list[CompanyInfo] = [ + { + "key": "open_sea", + "logo_image_name": "open_sea.svg", + "logo_alt": "OpenSea logo", + "name": "Alex Atallah", + "title": "Co-founder & CEO, OpenSea", + "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", + "profile_image": "/landing/social/alex_opensea.webp", + }, + { + "key": "fastly", + "logo_image_name": "fastly.svg", + "logo_alt": "Fastly logo", + "name": "Emanuele Bonura", + "title": "Senior SOC Engineer", + "quote": "Migrating our cybersecurity app from Streamlit to Reflex has been excellent. We quickly built a unified interface connecting BigQuery, Salesforce, and PagerDuty for our 15+ team members. The ease of use and rapid development, supported by your responsive team, made it a great experience.", + "profile_image": "", + }, + { + "key": "autodesk", + "logo_image_name": "autodesk.svg", + "logo_alt": "Autodesk logo", + "name": "Paolo", + "title": "Principal Consultant", + "quote": "One person can do the job of two with Reflex, so it cut our cost in half. I am able to wear all the caps at once: Solution Architecture, UI/UX, front-end and back-end.", + "profile_image": "", + }, + { + "key": "accenture", + "logo_image_name": "accenture.svg", + "logo_alt": "Accenture logo", + "name": "Jordan Lee", + "title": "Senior Automation Developer", + "quote": "Reflex let us automate workflows that were impossible with previous low-code platforms. We went from prototype to rollout in days, and our team loves writing real Python instead of fighting drag-and-drop UI pain.", + "profile_image": "", + }, +] + + +def quotes(): + return common_quotes( + companies=COMPANIES, + default_active_key="open_sea", + logo_base_path="/migration", + ) + + +__all__ = ["quotes"] diff --git a/pcweb/pages/migration/other_frameworks/__init__.py b/pcweb/pages/migration/other_frameworks/__init__.py new file mode 100644 index 000000000..c14ec3c1f --- /dev/null +++ b/pcweb/pages/migration/other_frameworks/__init__.py @@ -0,0 +1,46 @@ +import reflex as rx +import reflex_ui as ui + +from pcweb.components.hosting_banner import HostingBannerState +from pcweb.meta.meta import create_meta_tags +from pcweb.pages.about.views.divider import divider +from pcweb.pages.framework.views.footer_index import footer_index +from pcweb.pages.migration.other_frameworks.views import compare, explore, hero, quotes +from pcweb.views.marketing_navbar import marketing_navbar + + +@rx.page( + route="/migration/other-frameworks", + title="Switch from Other Frameworks to Reflex", + meta=create_meta_tags( + title="Switch from Other Frameworks to Reflex", + description="Switch from Other Frameworks to Reflex - The platform to build and scale enterprise apps", + image="/previews/low_code_migration_preview.webp", + ), +) +def other_frameworks_migration_page() -> rx.Component: + return rx.el.div( + marketing_navbar(), + rx.el.main( + rx.el.div( + hero(), + quotes(), + divider(), + compare(), + divider(), + explore(), + divider(), + footer_index(), + class_name="flex flex-col relative justify-center items-center w-full", + ), + class_name="flex flex-col w-full relative h-full justify-center items-center", + ), + class_name=ui.cn( + "flex flex-col w-full justify-center items-center relative dark:bg-m-slate-12 bg-m-slate-1", + rx.cond( + HostingBannerState.is_banner_visible, + "lg:pt-[7rem] pt-[3.5rem]", + "lg:pt-[4.5rem] pt-[3.5rem]", + ), + ), + ) diff --git a/pcweb/pages/migration/other_frameworks/views/__init__.py b/pcweb/pages/migration/other_frameworks/views/__init__.py new file mode 100644 index 000000000..f76295d0f --- /dev/null +++ b/pcweb/pages/migration/other_frameworks/views/__init__.py @@ -0,0 +1,6 @@ +from .compare import compare +from .explore import explore +from .hero import hero +from .quotes import quotes + +__all__ = ["compare", "explore", "hero", "quotes"] diff --git a/pcweb/pages/migration/other_frameworks/views/compare.py b/pcweb/pages/migration/other_frameworks/views/compare.py new file mode 100644 index 000000000..c431a0a7f --- /dev/null +++ b/pcweb/pages/migration/other_frameworks/views/compare.py @@ -0,0 +1,89 @@ +from pcweb.pages.migration.common.compare import ComparisonItem +from pcweb.pages.migration.common.compare import compare as common_compare + +COMPARISON_ITEMS: list[ComparisonItem] = [ + { + "title": "Full Control Without the Ceiling", + "icon": "CodesandboxIcon", + "pros": [ + "Write real Python — no ceiling on what you can build", + "Handle custom logic, complex data flows, and performance optimization natively", + "Build anything a full-stack app can do", + ], + "cons": [ + "Get you to v1 fast, then you hit walls", + "Custom logic and complex data flows require ugly workarounds", + "Platform limitations dictate what's possible, not your requirements", + ], + }, + { + "title": "You Own Your Code", + "icon": "SourceCodeSquareIcon", + "pros": [ + "It's your Python code — deploy it anywhere", + "Full version control with Git", + "Never hostage to a platform's pricing or shutdown", + ], + "cons": [ + "Your app lives on their infrastructure in their proprietary format", + "Vendor lock-in makes migration painful or impossible", + "Pricing changes or platform shutdowns put your app at risk", + ], + }, + { + "title": "Python Ecosystem Access", + "icon": "PythonIcon", + "pros": [ + "Use libraries you already know — pandas, scikit-learn, whatever", + "Build internal tools or customer-facing apps in one language", + "Leverage the entire Python ecosystem with no restrictions", + ], + "cons": [ + "Limited to the platform's pre-built integrations", + "Can't tap into Python's ML, data science, or backend libraries", + "Forces non-JS developers to learn new tools or work around limitations", + ], + }, + { + "title": "Scales With Complexity", + "icon": "SquareArrowExpand02Icon", + "pros": [ + "Auth flows, real-time features, complex state management — all native", + "Handles growing complexity because it's just code", + "No artificial boundaries on what you can build", + ], + "cons": [ + "Great for simple CRUD apps and dashboards, then it breaks down", + "Once you need real complexity, you're fighting the tool instead of building", + "Workarounds pile up and become unmaintainable", + ], + }, + { + "title": "Team Collaboration & Engineering Practices", + "icon": "UserSwitchIcon", + "pros": [ + "Fits into normal engineering workflows — Git, PRs, CI/CD", + "Code review and automated testing work out of the box", + "Your whole team can collaborate using standard dev practices", + ], + "cons": [ + "Version control is difficult or impossible", + "Code review and testing are afterthoughts at best", + "Engineering best practices don't apply to proprietary drag-and-drop formats", + ], + }, +] + + +def compare(): + return common_compare( + kicker="Compare", + heading_lines=["How You Benefit ", "With Reflex vs. ", "Other Approaches"], + description="Other frameworks get you started fast, but Reflex lets you finish. Here's how Reflex compares to platforms like React, JavaScript, Django, and FastAPI.", + top_left_title="Reflex", + top_right_title="React, JavaScript, Django, FastAPI", + comparison_items=COMPARISON_ITEMS, + ) + + +__all__ = ["compare"] diff --git a/pcweb/pages/migration/other_frameworks/views/explore.py b/pcweb/pages/migration/other_frameworks/views/explore.py new file mode 100644 index 000000000..18ca500f7 --- /dev/null +++ b/pcweb/pages/migration/other_frameworks/views/explore.py @@ -0,0 +1,48 @@ +from pcweb.pages.migration.common.explore import Feature +from pcweb.pages.migration.common.explore import explore as common_explore + +FEATURES: list[Feature] = [ + { + "title": "AI-Assisted App Development", + "description": "Generate UI and app logic faster with AI while keeping full code control.", + "icon": "ArtificialIntelligence04Icon", + }, + { + "title": "Real Python, Not Drag-and-Drop", + "description": "Write maintainable Python code that fits your existing engineering standards.", + "icon": "SourceCodeSquareIcon", + }, + { + "title": "Data Stack Integrations", + "description": "Connect databases, APIs, and internal services without low-code workarounds.", + "icon": "DatabaseIcon", + }, + { + "title": "Security-First by Design", + "description": "Deploy with full control over infrastructure, access, and compliance boundaries.", + "icon": "ShieldEnergyIcon", + }, + { + "title": "Production-Grade Performance", + "description": "Scale from internal tools to enterprise apps with robust backend capabilities.", + "icon": "DashboardSpeed01Icon", + }, + { + "title": "Team-Friendly Workflows", + "description": "Use Git, PR reviews, and CI/CD workflows your team already trusts.", + "icon": "UserSwitchIcon", + }, +] + + +def explore(): + return common_explore( + kicker="Explore", + title_prefix="Build Your App With the Speed of ", + title_suffix="AI and Precision of Python", + description="Reflex is growing—and we're looking for people who care deeply about developer experience, clean abstractions.", + features=FEATURES, + ) + + +__all__ = ["explore"] diff --git a/pcweb/pages/migration/other_frameworks/views/hero.py b/pcweb/pages/migration/other_frameworks/views/hero.py new file mode 100644 index 000000000..af55b0648 --- /dev/null +++ b/pcweb/pages/migration/other_frameworks/views/hero.py @@ -0,0 +1,39 @@ +from pcweb.pages.migration.common.hero import HeroLogo +from pcweb.pages.migration.common.hero import hero as common_hero + +HERO_LOGOS: list[HeroLogo] = [ + { + "image_name": "react.svg", + "alt": "React Logo", + "class_name": "top-[9.5rem] -ml-[10.5rem]", + }, + { + "image_name": "js.svg", + "alt": "JavaScript Logo", + "class_name": "top-[15rem] ml-[16.5rem]", + }, + { + "image_name": "django.svg", + "alt": "Django Logo", + "class_name": "top-[9.5rem] ml-[10.5rem]", + }, + { + "image_name": "fastapi.svg", + "alt": "FastAPI Logo", + "class_name": "top-[15rem] -ml-[16.5rem]", + }, +] + + +def hero(): + return common_hero( + kicker="Move From Other Frameworks to Reflex", + title="The Next-Gen Platform Built for Modern Enterprises", + subtitle="Escape other framework constraints without sacrificing speed. Build production-grade apps in pure Python with complete control over your stack.", + cta_text="Book a Demo", + logos=HERO_LOGOS, + logo_base_path="/migration", + ) + + +__all__ = ["hero"] diff --git a/pcweb/pages/migration/other_frameworks/views/quotes.py b/pcweb/pages/migration/other_frameworks/views/quotes.py new file mode 100644 index 000000000..04ea15888 --- /dev/null +++ b/pcweb/pages/migration/other_frameworks/views/quotes.py @@ -0,0 +1,52 @@ +from pcweb.pages.migration.common.quotes import CompanyInfo +from pcweb.pages.migration.common.quotes import quotes as common_quotes + +COMPANIES: list[CompanyInfo] = [ + { + "key": "open_sea", + "logo_image_name": "open_sea.svg", + "logo_alt": "OpenSea logo", + "name": "Alex Atallah", + "title": "Co-founder & CEO, OpenSea", + "quote": "Have been playing with Reflex since January and realized I should just say, from a fellow YC member: love the architecture decisions you guys are making!", + "profile_image": "/landing/social/alex_opensea.webp", + }, + { + "key": "fastly", + "logo_image_name": "fastly.svg", + "logo_alt": "Fastly logo", + "name": "Emanuele Bonura", + "title": "Senior SOC Engineer", + "quote": "Migrating our cybersecurity app from Streamlit to Reflex has been excellent. We quickly built a unified interface connecting BigQuery, Salesforce, and PagerDuty for our 15+ team members. The ease of use and rapid development, supported by your responsive team, made it a great experience.", + "profile_image": "", + }, + { + "key": "autodesk", + "logo_image_name": "autodesk.svg", + "logo_alt": "Autodesk logo", + "name": "Paolo", + "title": "Principal Consultant", + "quote": "One person can do the job of two with Reflex, so it cut our cost in half. I am able to wear all the caps at once: Solution Architecture, UI/UX, front-end and back-end.", + "profile_image": "", + }, + { + "key": "accenture", + "logo_image_name": "accenture.svg", + "logo_alt": "Accenture logo", + "name": "Jordan Lee", + "title": "Senior Automation Developer", + "quote": "Reflex let us automate workflows that were impossible with previous low-code platforms. We went from prototype to rollout in days, and our team loves writing real Python instead of fighting drag-and-drop UI pain.", + "profile_image": "", + }, +] + + +def quotes(): + return common_quotes( + companies=COMPANIES, + default_active_key="open_sea", + logo_base_path="/migration", + ) + + +__all__ = ["quotes"] diff --git a/pcweb/views/marketing_navbar.py b/pcweb/views/marketing_navbar.py index 8d56d738a..80b639d34 100644 --- a/pcweb/views/marketing_navbar.py +++ b/pcweb/views/marketing_navbar.py @@ -387,12 +387,12 @@ def solutions_content() -> rx.Component: ( "Switch from Other Frameworks", "CodeIcon", - use_cases_page.path, + "/migration/other-frameworks", ), ( "Switch from Other AI tools", "ArtificialIntelligence04Icon", - use_cases_page.path, + "/migration/other-ai-tools", ), ], ),