From aef21a810207cd443f8b385f9242650a85cb19b9 Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 25 Feb 2026 17:34:39 +0100 Subject: [PATCH 1/3] ENG-8908: Blog cards updates --- blog/2023-06-28-rebrand-to-reflex.md | 1 + blog/2023-08-02-seed-annoucement.md | 1 + ...ing-new-workflows-with-background-tasks.md | 1 + blog/2023-10-11-graphing-update.md | 1 + ...-10-25-implementing-sign-in-with-google.md | 1 + blog/2024-02-16-reflex-v0-4-0.md | 1 + blog/2024-03-21-reflex-architecture.md | 1 + blog/2024-03-27-structuring-a-large-app.md | 1 + blog/2024-04-16-custom-components.md | 1 + blog/2024-06-28-using-table-component.md | 1 + blog/2024-09-21-reflex-v060.md | 1 + blog/2024-09-25-using-ag-grid-in-reflex.md | 1 + ...24-10-8-self-hosting-reflex-with-docker.md | 1 + ...24-11-19-microsoft-azure-authentication.md | 1 + blog/2024-12-05-reflex-cloud.md | 1 + blog/2024-12-20-python-comparison.md | 1 + ...-27-top-10-data-visualization-libraries.md | 1 + blog/2025-05-16-top-5-ai-app-builders.md | 1 + .../2025-06-03-internal-tool-builders-2025.md | 1 + blog/2025-06-20-reflex-dash.md | 1 + ...025-06-30-reflex-databricks-partnership.md | 1 + blog/2025-07-01-reflex-080.md | 1 + blog/2025-08-20-reflex-streamlit.md | 1 + blog/2025-09-03-reflex-jupyter.md | 1 + blog/2025-12-05-on-premises-deployment.md | 1 + ...7-top-7-enterprise-ai-app-builders-2026.md | 1 + ...26-01-09-top-python-web-frameworks-2026.md | 1 + pcweb/pages/__init__.py | 2 +- pcweb/pages/about/__init__.py | 38 ++- pcweb/pages/about/views/hero.py | 9 +- pcweb/pages/blog/blog.py | 252 ++++++++++-------- pcweb/pages/blog/page.py | 10 +- pcweb/templates/marketing_page.py | 11 +- 33 files changed, 208 insertions(+), 141 deletions(-) diff --git a/blog/2023-06-28-rebrand-to-reflex.md b/blog/2023-06-28-rebrand-to-reflex.md index a1f1ad2f76..01335e415a 100644 --- a/blog/2023-06-28-rebrand-to-reflex.md +++ b/blog/2023-06-28-rebrand-to-reflex.md @@ -4,6 +4,7 @@ date: 2023-06-28 title: Pynecone is now Reflex description: We have some exciting news to share about the next stage of our company! image: /reflex_banner.png +tag: Announcements meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2023-08-02-seed-annoucement.md b/blog/2023-08-02-seed-annoucement.md index 3018f79c1c..b4edc487dc 100644 --- a/blog/2023-08-02-seed-annoucement.md +++ b/blog/2023-08-02-seed-annoucement.md @@ -4,6 +4,7 @@ date: 2023-08-02 title: Announcing our Seed Round description: Reflex has raised a $5M seed led by Lux Capital. image: /blog/fundraise_dark.webp +tag: Announcements meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2023-09-28-unlocking-new-workflows-with-background-tasks.md b/blog/2023-09-28-unlocking-new-workflows-with-background-tasks.md index 13d9d5b3bc..aac39927a7 100644 --- a/blog/2023-09-28-unlocking-new-workflows-with-background-tasks.md +++ b/blog/2023-09-28-unlocking-new-workflows-with-background-tasks.md @@ -4,6 +4,7 @@ date: 2023-09-28 title: Unlocking New Workflows with Background Tasks description: What is a background task and how can it help you build better apps? image: /blog/background_tasks.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2023-10-11-graphing-update.md b/blog/2023-10-11-graphing-update.md index 3478cd3a0a..3e76a05385 100644 --- a/blog/2023-10-11-graphing-update.md +++ b/blog/2023-10-11-graphing-update.md @@ -4,6 +4,7 @@ date: 2023-10-11 title: "New Core Graphing Components" description: "Using Reflex's new core graphing feature to build a live streaming graphing app." image: /blog/graphing.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2023-10-25-implementing-sign-in-with-google.md b/blog/2023-10-25-implementing-sign-in-with-google.md index 667ada5dbe..06aac4fd97 100644 --- a/blog/2023-10-25-implementing-sign-in-with-google.md +++ b/blog/2023-10-25-implementing-sign-in-with-google.md @@ -4,6 +4,7 @@ date: 2023-10-25 title: "Implementing Sign In with Google" description: "How to wrap a third-party auth component and integrate it into a Reflex app." image: /blog/google_auth.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2024-02-16-reflex-v0-4-0.md b/blog/2024-02-16-reflex-v0-4-0.md index 982ac725b5..79c34c19e9 100644 --- a/blog/2024-02-16-reflex-v0-4-0.md +++ b/blog/2024-02-16-reflex-v0-4-0.md @@ -4,6 +4,7 @@ date: 2024-02-16 title: "Reflex v0.4.0" description: "New features coming in the 0.4.0 release." image: /blog/reflex-040.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2024-03-21-reflex-architecture.md b/blog/2024-03-21-reflex-architecture.md index bc05dc8e33..c49c765857 100644 --- a/blog/2024-03-21-reflex-architecture.md +++ b/blog/2024-03-21-reflex-architecture.md @@ -4,6 +4,7 @@ date: 2024-03-21 title: Designing a Pure Python Web Framework description: A look at how Reflex works under the hood. image: /blog/web_framework.webp +tag: Open Source meta: [ {"name": "keywords", "content": "python web app, python web app framework, web app framework python, python web apps, web app python, react python websocket, python react, react python, react with python, python and react js, react js with python, python and react, react js python"}, ] diff --git a/blog/2024-03-27-structuring-a-large-app.md b/blog/2024-03-27-structuring-a-large-app.md index a698e4287d..e3a4fe122e 100644 --- a/blog/2024-03-27-structuring-a-large-app.md +++ b/blog/2024-03-27-structuring-a-large-app.md @@ -4,6 +4,7 @@ date: 2024-03-27 title: "Structuring a Large App" description: "So your Reflex app is getting large? Here's some advice on how to lay it out." image: /blog/project_structure.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2024-04-16-custom-components.md b/blog/2024-04-16-custom-components.md index 89378ebe58..56fd077666 100644 --- a/blog/2024-04-16-custom-components.md +++ b/blog/2024-04-16-custom-components.md @@ -4,6 +4,7 @@ date: 2024-04-16 title: Custom Components description: Announcing our custom component ecosystem. image: /blog/custom_components.webp +tag: Open Source meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2024-06-28-using-table-component.md b/blog/2024-06-28-using-table-component.md index 0ca775b978..86b64a829c 100644 --- a/blog/2024-06-28-using-table-component.md +++ b/blog/2024-06-28-using-table-component.md @@ -4,6 +4,7 @@ date: 2024-06-28 title: Creating Tables in Reflex description: Describing main uses of the Table component with a Database image: /blog/table.webp +tag: Open Source meta: [ {"name": "keywords", "content": "data table, table data, data tables, data table example, python data table, python table"}, ] diff --git a/blog/2024-09-21-reflex-v060.md b/blog/2024-09-21-reflex-v060.md index 326d9c4c07..17416247a2 100644 --- a/blog/2024-09-21-reflex-v060.md +++ b/blog/2024-09-21-reflex-v060.md @@ -4,6 +4,7 @@ date: 2024-09-21 title: Reflex v0.6.0 description: New features and improvements in Reflex v0.6.0 image: /blog/reflex-060.webp +tag: Open Source meta: [ {"name": "keywords", "content": "web application framework in python, python gui library, python web framework, reflex python"}, ] diff --git a/blog/2024-09-25-using-ag-grid-in-reflex.md b/blog/2024-09-25-using-ag-grid-in-reflex.md index 6ba06e797c..7140e13b6f 100644 --- a/blog/2024-09-25-using-ag-grid-in-reflex.md +++ b/blog/2024-09-25-using-ag-grid-in-reflex.md @@ -4,6 +4,7 @@ date: 2024-10-03 title: AG Grid in Reflex description: Getting started with powerful data tables in your Python web apps image: /blog/ag-grid.webp +tag: Open Source meta: [ { "name": "keywords", diff --git a/blog/2024-10-8-self-hosting-reflex-with-docker.md b/blog/2024-10-8-self-hosting-reflex-with-docker.md index fa2c97f52b..ec2be05141 100644 --- a/blog/2024-10-8-self-hosting-reflex-with-docker.md +++ b/blog/2024-10-8-self-hosting-reflex-with-docker.md @@ -4,6 +4,7 @@ date: 2024-10-08 title: Self Hosting Reflex with Docker description: Hosting Reflex on your own infra using Docker for efficient containerization. image: /blog/self-hosting-with-docker.webp +tag: Open Source meta: [ { "name": "keywords", diff --git a/blog/2024-11-19-microsoft-azure-authentication.md b/blog/2024-11-19-microsoft-azure-authentication.md index d32a72bce7..7f65f85be3 100644 --- a/blog/2024-11-19-microsoft-azure-authentication.md +++ b/blog/2024-11-19-microsoft-azure-authentication.md @@ -4,6 +4,7 @@ date: 2024-11-19 title: Microsoft Azure Auth description: Implementing Microsoft Azure Single Sign-On (SSO) Auth in Reflex app. image: /blog/azure_auth.webp +tag: Open Source meta: [ { "name": "keywords", diff --git a/blog/2024-12-05-reflex-cloud.md b/blog/2024-12-05-reflex-cloud.md index 959417fcbb..0c8518cfba 100644 --- a/blog/2024-12-05-reflex-cloud.md +++ b/blog/2024-12-05-reflex-cloud.md @@ -4,6 +4,7 @@ date: 2024-12-05 title: Reflex Cloud description: Deploy your Reflex apps with a single command image: /blog/reflex-cloud.webp +tag: Cloud meta: [ {"name": "keywords", "content": ""}, ] diff --git a/blog/2024-12-20-python-comparison.md b/blog/2024-12-20-python-comparison.md index f766c77d73..c66e591922 100644 --- a/blog/2024-12-20-python-comparison.md +++ b/blog/2024-12-20-python-comparison.md @@ -4,6 +4,7 @@ date: 2024-12-20 title: Top Python Web Development Frameworks in 2025 description: Reflex vs Django vs Flask vs Gradio vs Streamlit vs Dash vs FastAPI image: /blog/top_python_web_frameworks.webp +tag: Builder meta: [ {"name": "keywords", "content": "streamlit python, streamlit, streamlit alternatives, plotly, dash app, plotly python, fastapi"}, ] diff --git a/blog/2025-01-27-top-10-data-visualization-libraries.md b/blog/2025-01-27-top-10-data-visualization-libraries.md index 5a8418e37f..6fa36a3825 100644 --- a/blog/2025-01-27-top-10-data-visualization-libraries.md +++ b/blog/2025-01-27-top-10-data-visualization-libraries.md @@ -4,6 +4,7 @@ date: 2025-01-27 title: Top 10 Python Data Visualization Libraries in 2025 description: Matplotlib vs Seaborn vs Plotly vs Bokeh vs Altair vs GeoPandas vs HoloViews vs Pygal vs Geoplotlib vs GGPlot image: /blog/data_visualization_blog.webp +tag: Builder meta: [ {"name": "keywords", "content": "matplotlib, seaborn, plotly, bokeh, altair, geopandas, holoviews, pygal, Geoplotlib, ggplot"}, ] diff --git a/blog/2025-05-16-top-5-ai-app-builders.md b/blog/2025-05-16-top-5-ai-app-builders.md index 7cc14ca929..cf8f1bf2c3 100644 --- a/blog/2025-05-16-top-5-ai-app-builders.md +++ b/blog/2025-05-16-top-5-ai-app-builders.md @@ -4,6 +4,7 @@ date: 2025-05-16 title: Top AI App Builders in 2025 description: Reflex.build vs Replit Agent vs v0.dev vs Bolt.new vs Lovable.dev image: /blog/ai_app_builders.webp +tag: Builder meta: [ {"name": "keywords", "content": "Reflex vs Replit, v0.dev alternatives, build AI apps, no-code AI tools, Replit agent, AI developer tools, app builders for developers, AI UI generators, v0.dev review, Replit AI tools, Lovable.dev, Bolt.new, AI in software development, full-stack AI apps"} ] diff --git a/blog/2025-06-03-internal-tool-builders-2025.md b/blog/2025-06-03-internal-tool-builders-2025.md index cf63728377..7a70483962 100644 --- a/blog/2025-06-03-internal-tool-builders-2025.md +++ b/blog/2025-06-03-internal-tool-builders-2025.md @@ -4,6 +4,7 @@ date: 2025-06-03 title: The 10 best internal tool builders in 2025 description: Reflex vs Retool vs Budibase vs Appsmith vs ToolJet vs Zapier Interfaces vs Glide vs Softr vs DronaHQ vs Microsoft Power Apps image: /blog/10_best_internal_tool_builders.webp +tag: Builder meta: [ {"name": "keywords", "content": "Reflex vs Retool, internal tool builders 2025, Appsmith alternatives, Budibase vs ToolJet, low-code tools, no-code internal apps, best internal app platforms, enterprise internal tools, Power Apps review, build dashboards, admin panel builders, internal tool comparison, open source internal tools, Zapier Interfaces, Glide app builder, DronaHQ, Softr"} ] diff --git a/blog/2025-06-20-reflex-dash.md b/blog/2025-06-20-reflex-dash.md index d5468aa667..76d572da78 100644 --- a/blog/2025-06-20-reflex-dash.md +++ b/blog/2025-06-20-reflex-dash.md @@ -4,6 +4,7 @@ date: 2025-06-20 title: Reflex vs Plotly Dash description: A Comparison of Python Frameworks for Building Interactive Financial Dashboards image: /blog/reflex-dash.webp +tag: Builder meta: [ { "name": "keywords", diff --git a/blog/2025-06-30-reflex-databricks-partnership.md b/blog/2025-06-30-reflex-databricks-partnership.md index a89a3d7520..17f609bea4 100644 --- a/blog/2025-06-30-reflex-databricks-partnership.md +++ b/blog/2025-06-30-reflex-databricks-partnership.md @@ -4,6 +4,7 @@ date: 2025-06-30 title: "Reflex x Databricks: Python-Powered Vibe Coding for Enterprises" description: "We're excited to announce our official partnership with Databricks and the launch of our Reflex AI Builder integration with Databricks Apps." image: /blog/databricks-partnership.webp +tag: Announcements meta: [ {"name": "keywords", "content": "Reflex Databricks, Python apps, enterprise development, AI Builder, Unity Catalog, data apps, Python framework, Databricks Apps"} ] diff --git a/blog/2025-07-01-reflex-080.md b/blog/2025-07-01-reflex-080.md index 67a4040d98..ac2aefc772 100644 --- a/blog/2025-07-01-reflex-080.md +++ b/blog/2025-07-01-reflex-080.md @@ -4,6 +4,7 @@ date: 2025-07-01 title: Reflex v0.8.0 - Refactoring for Performance description: Major improvements in Reflex v0.8.0 developer experience and performance. image: /blog/080.webp +tag: Open Source meta: [ {"name": "keywords", "content": "reflex python, web framework, nextjs migration, react router, vite, pydantic replacement, performance improvements"}, ] diff --git a/blog/2025-08-20-reflex-streamlit.md b/blog/2025-08-20-reflex-streamlit.md index 35baed47b9..49eaf48ca0 100644 --- a/blog/2025-08-20-reflex-streamlit.md +++ b/blog/2025-08-20-reflex-streamlit.md @@ -4,6 +4,7 @@ date: 2025-08-20 title: Reflex vs Streamlit description: "A head‑to‑head comparison of two Python frameworks for building fast, interactive web apps" image: /blog/reflex-streamlit.webp +tag: Builder meta: [ { "name": "keywords", diff --git a/blog/2025-09-03-reflex-jupyter.md b/blog/2025-09-03-reflex-jupyter.md index bc738a2eb5..1714e363a7 100644 --- a/blog/2025-09-03-reflex-jupyter.md +++ b/blog/2025-09-03-reflex-jupyter.md @@ -4,6 +4,7 @@ date: 2025-09-03 title: Turn Jupyter Notebooks into Production Dashboards in Python description: "Convert Jupyter notebooks into interactive, production-ready dashboards with Python and Reflex. Keep your pandas logic, add UI, and deploy in minutes." image: /blog/jupyter_reflex.png +tag: Open Source meta: [ { "name": "keywords", diff --git a/blog/2025-12-05-on-premises-deployment.md b/blog/2025-12-05-on-premises-deployment.md index 49b12a5314..2956fcef26 100644 --- a/blog/2025-12-05-on-premises-deployment.md +++ b/blog/2025-12-05-on-premises-deployment.md @@ -4,6 +4,7 @@ date: 2025-12-05 title: "Reflex Build On-Prem: The Secure AI App Builder That Runs Inside Your Company Infrastructure" description: An enterprise, on-premises AI app builder for developing secure internal tools and dashboards using Python. image: /blog/on-prem.png +tag: Cloud meta: [ { "name": "keywords", diff --git a/blog/2025-12-17-top-7-enterprise-ai-app-builders-2026.md b/blog/2025-12-17-top-7-enterprise-ai-app-builders-2026.md index 19b2dd6dd2..78a794246b 100644 --- a/blog/2025-12-17-top-7-enterprise-ai-app-builders-2026.md +++ b/blog/2025-12-17-top-7-enterprise-ai-app-builders-2026.md @@ -4,6 +4,7 @@ date: 2025-12-17 title: "Top 7 Enterprise AI App Builders in 2026: A Practical Comparison" description: Reflex.build vs Microsoft Power Apps vs Superblocks vs Blaze.tech vs Knack vs Lovable.dev vs Replit Agent image: /blog/top7.webp +tag: Builder meta: [ { "name": "keywords", diff --git a/blog/2026-01-09-top-python-web-frameworks-2026.md b/blog/2026-01-09-top-python-web-frameworks-2026.md index db7cb25351..a7395f47a2 100644 --- a/blog/2026-01-09-top-python-web-frameworks-2026.md +++ b/blog/2026-01-09-top-python-web-frameworks-2026.md @@ -4,6 +4,7 @@ date: 2026-01-09 title: Top Python Web Development Frameworks in 2026 description: Reflex vs Django vs Flask vs Gradio vs Streamlit vs Dash vs FastAPI image: /blog/top_python_web_frameworks_2026.png +tag: Builder meta: [ {"name": "keywords", "content": "streamlit python, streamlit, streamlit alternatives, plotly, dash app, plotly python, fastapi"}, ] diff --git a/pcweb/pages/__init__.py b/pcweb/pages/__init__.py index 15c76d6195..02455eb094 100644 --- a/pcweb/pages/__init__.py +++ b/pcweb/pages/__init__.py @@ -1,6 +1,6 @@ from pcweb.route import Route -from .about import about # noqa: F401 +from .about import about_page as about_page from .blog import blog_routes from .booked import booked as booked from .check_your_email_demo import page_thank_you as page_thank_you diff --git a/pcweb/pages/about/__init__.py b/pcweb/pages/about/__init__.py index fbaeb814e0..3d25e5d6af 100644 --- a/pcweb/pages/about/__init__.py +++ b/pcweb/pages/about/__init__.py @@ -12,11 +12,11 @@ team, ) from pcweb.pages.framework.views.footer_index import footer_index -from pcweb.views.marketing_navbar import marketing_navbar +from pcweb.templates.marketing_page import marketing_page -@rx.page( - route="/about", +@marketing_page( + path="/about", title="Reflex · About", meta=create_meta_tags( title="Reflex · About", @@ -24,24 +24,20 @@ image="/previews/index_preview.webp", ), ) -def about() -> rx.Component: +def about_page() -> rx.Component: return rx.el.div( - marketing_navbar(), - rx.el.main( - rx.el.div( - hero(), - companies(), - square_logo(), - cards(), - divider(), - hiring(), - team(), - news(), - 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", + rx.el.div( + hero(), + companies(), + square_logo(), + cards(), + divider(), + hiring(), + team(), + news(), + divider(), + footer_index(), + class_name="flex flex-col relative justify-center items-center w-full", ), - class_name="flex flex-col w-full justify-center items-center relative dark:bg-m-slate-12 bg-m-slate-1", + class_name="flex flex-col w-full relative h-full justify-center items-center", ) diff --git a/pcweb/pages/about/views/hero.py b/pcweb/pages/about/views/hero.py index 93d6144ce7..3ccfca931f 100644 --- a/pcweb/pages/about/views/hero.py +++ b/pcweb/pages/about/views/hero.py @@ -1,8 +1,6 @@ import reflex as rx import reflex_ui as ui -from pcweb.components.hosting_banner import HostingBannerState - def hero() -> rx.Component: return rx.el.section( @@ -92,11 +90,6 @@ def hero() -> rx.Component: class_name="flex flex-col gap-8 lg:p-12 p-6 border border-m-slate-4 dark:border-m-slate-10 shadow-[0_6px_16px_0_rgba(0,0,0,0.04)_inset] dark:shadow-none dark:bg-[linear-gradient(212deg,var(--m-slate-12)_0%,var(--m-slate-11)_100%)] lg:max-w-[34.5rem] w-full relative", ), class_name=ui.cn( - "flex lg:flex-row flex-col max-w-(--layout-max-width) mx-auto lg:px-24 px-6 overflow-hidden", - rx.cond( - HostingBannerState.is_banner_visible, - "lg:pt-[14.5rem] pt-[12.5rem]", - "lg:pt-[10.5rem] pt-[7.5rem]", - ), + "flex lg:flex-row flex-col max-w-(--layout-max-width) mx-auto lg:px-24 px-6 overflow-hidden pt-24", ), ) diff --git a/pcweb/pages/blog/blog.py b/pcweb/pages/blog/blog.py index f40d32e82e..3b56647975 100644 --- a/pcweb/pages/blog/blog.py +++ b/pcweb/pages/blog/blog.py @@ -1,158 +1,200 @@ import reflex as rx +import reflex_ui as ui +from reflex.experimental.client_state import ClientStateVar -from pcweb.components.icons.icons import get_icon -from pcweb.components.webpage.comps import h1_title +from pcweb.components.hosting_banner import HostingBannerState +from pcweb.components.marketing_button import button as marketing_button from pcweb.meta.meta import create_meta_tags +from pcweb.signup import IndexState from pcweb.templates.marketing_page import marketing_page -from pcweb.templates.webpage import webpage from .page import page from .paths import blog_data +blog_filter_cs = ClientStateVar.create("blog_filter", default="All") -def first_post_card(meta: dict, path: str) -> rx.Component: - return rx.link( - rx.box( - rx.image( - src=meta["image"], - loading="eager", - custom_attrs={"fetchPriority": "high"}, - alt="Image preview for blog post: " + str(meta["title"]), - class_name="group-hover:scale-105 w-full h-full transition-transform duration-150 ease-out object-left object-cover", +FILTERS = ["All", "Announcements", "Open Source", "Builder", "Cloud"] + + +def filter_button(filter: str) -> rx.Component: + active_cn = "shadow-[inset_0_-1px_0_0_var(--primary-10)] text-primary-10 dark:text-primary-9" + return marketing_button( + filter, + variant="ghost", + size="sm", + on_click=blog_filter_cs.set_value(filter), + class_name=ui.cn( + "h-full rounded-none", + rx.cond(blog_filter_cs.value == filter, active_cn, ""), + ), + ) + + +def blog_filters_row() -> rx.Component: + return rx.el.div( + rx.el.div( + *[filter_button(filter) for filter in FILTERS], + class_name="flex flex-row gap-2 items center px-4 h-full", + ), + newletter_input(), + class_name=ui.cn( + "flex flex-row gap-4 items-center justify-between h-[3.25rem] border-x border-m-slate-4 dark:border-m-slate-10 bg-m-slate-1 dark:bg-m-slate-11 [box-shadow:0_-1px_0_0_rgba(0,_0,_0,_0.05),_0_-2px_2px_1px_rgba(0,_0,_0,_0.02),_0_1px_1px_0_rgba(0,_0,_0,_0.08),_0_4px_8px_0_rgba(0,_0,_0,_0.03),_0_1px_0_0_#FFF_inset] w-full z-10 lg:sticky dark:shadow-none max-lg:overflow-x-auto max-lg:-mx-4 max-lg:w-[calc(100%+2rem)]", + rx.cond( + HostingBannerState.is_banner_visible, + "lg:top-[7rem]", + "lg:top-[4.5rem]", ), - class_name="relative flex-shrink-0 w-2/5 h-[18rem] overflow-hidden border-r border-solid border-slate-5", ), - rx.box( - rx.box( - rx.el.h4( - meta["title"], - class_name="font-x-large text-slate-12", + ) + + +def newletter_input() -> rx.Component: + return rx.el.div( + rx.cond( + IndexState.signed_up, + rx.el.div( + rx.el.div( + rx.icon( + tag="circle-check", + size=16, + class_name="!text-violet-9", + ), + rx.text( + "Thanks for subscribing!", + class_name="text-sm text-m-slate-7 dark:text-m-slate-6 font-medium", + ), + class_name="flex flex-row items-center gap-2", ), - rx.moment( - str(meta["date"]), - format="MMM DD, YYYY", - class_name="font-normal text-secondary-11 text-sm", + marketing_button( + "Sign up for another email", + variant="outline", + size="md", + on_click=IndexState.signup_for_another_user, ), - class_name="flex flex-col gap-1 p-[0.625rem_0.75rem_0rem_0.75rem] w-full", + class_name="flex flex-row items-center gap-2", ), - rx.box( - rx.text( - meta["description"], - class_name="line-clamp-2 font-base text-secondary-11", + rx.form( + rx.el.input( + placeholder="Email", + name="input_email", + type="email", + required=True, + class_name="relative [box-shadow:0_-1px_0_0_rgba(0,_0,_0,_0.08)_inset,_0_0_0_1px_rgba(0,_0,_0,_0.08)_inset,_0_1px_2px_0_rgba(0,_0,_0,_0.02),_0_1px_4px_0_rgba(0,_0,_0,_0.02)] rounded-lg h-9 px-2.5 py-1.5 w-[12rem] text-sm placeholder:text-m-slate-7 dark:placeholder:text-m-slate-6 font-[525] focus:outline-none outline-none dark:border dark:border-m-slate-10 bg-white dark:bg-m-slate-12", ), - rx.box( - rx.text( - meta["author"], - class_name="font-small text-secondary-11 truncate overflow-hidden text-ellipsis max-w-[50%] min-w-0 flex-shrink", - ), - rx.el.button( - rx.text( - "Read more", - class_name="font-small text-secondary-11", - ), - get_icon(icon="new_tab", class_name="p-[5px]"), - class_name="flex items-center border-slate-5 bg-slate-1 hover:bg-slate-3 shadow-small pl-[5px] border rounded-md w-auto max-w-full text-secondary-11 transition-bg cursor-pointer overflow-hidden flex-shrink-0", - ), - class_name="flex flex-row justify-between items-center gap-1 min-w-0 w-full h-auto", + marketing_button( + "Get Updates", + type="submit", + variant="outline", + size="md", + class_name="w-fit max-w-full", ), - class_name="flex flex-col justify-between p-[0rem_0.75rem_0.75rem_0.75rem] w-full h-full", + class_name="w-full flex flex-col lg:flex-row gap-2 lg:items-center items-start max-lg:hidden", + on_submit=IndexState.signup, ), - class_name="flex flex-col gap-[10px] w-full h-auto", ), - href=path, - is_external=False, - underline="none", - class_name="box-border lg:flex flex-row flex-shrink-0 gap-3 border-slate-5 hidden bg-slate-1 shadow-large border border-solid rounded-xl w-full overflow-hidden group", + class_name="ml-auto mr-2", ) def card_content(meta: dict, path: str) -> rx.Component: - return rx.link( - rx.box( - rx.image( - src=meta["image"], - loading="eager", - custom_attrs={"fetchPriority": "high"}, - alt="Image preview for blog post: " + str(meta["title"]), - class_name="group-hover:scale-105 w-full h-full transition-transform duration-150 ease-out object-left object-cover", + return rx.el.div( + rx.el.a( + rx.el.div( + rx.image( + src=meta["image"], + loading="eager", + custom_attrs={"fetchPriority": "high"}, + alt="Image preview for blog post: " + str(meta["title"]), + class_name="group-hover:scale-105 w-full h-full transition-transform duration-150 ease-out object-top object-cover", + ), + class_name="relative flex-shrink-0 border-slate-5 border-b border-solid w-full h-[19.5rem] overflow-hidden", ), - class_name="relative flex-shrink-0 border-slate-5 border-b border-solid w-full h-[13.5rem] overflow-hidden", - ), - rx.box( - rx.box( + rx.el.div( rx.el.span( meta["title"], - class_name="font-smbold text-slate-12", - ), - rx.moment( - str(meta["date"]), - format="MMM DD, YYYY", - class_name="font-normal text-secondary-11 text-xs", + class_name="text-2xl font-[575] text-m-slate-12 dark:text-m-slate-3 mb-4", ), - class_name="flex flex-col gap-1 p-[0.625rem_0.75rem_0rem_0.75rem] w-full", - ), - rx.box( - rx.text( + rx.el.p( meta["description"], - class_name="line-clamp-2 font-small text-secondary-11", + class_name="text-m-slate-7 dark:text-m-slate-6 text-sm font-[475] mb-6", ), - rx.box( - rx.text( - meta["author"], - class_name="font-small text-secondary-11 truncate overflow-hidden text-ellipsis max-w-[50%] min-w-0 flex-shrink", - ), - rx.el.button( - rx.text( - "Read more", - class_name="font-small text-secondary-11", - ), - get_icon(icon="new_tab", class_name="p-[5px]"), - class_name="flex items-center border-slate-5 bg-slate-1 hover:bg-slate-3 shadow-small pl-[5px] border rounded-md w-auto max-w-full text-secondary-11 transition-bg cursor-pointer overflow-hidden", - ), - class_name="flex flex-row justify-between items-center gap-1 w-full h-auto", + rx.el.span( + meta["author"], + class_name="text-m-slate-12 dark:text-m-slate-3 text-sm font-[525] mt-auto", ), - class_name="flex flex-col justify-between p-[0rem_0.75rem_0.75rem_0.75rem] w-full h-full", + class_name="flex flex-col w-full h-full pb-12 px-12", ), - class_name="flex flex-col gap-[10px] w-full h-full", + to=path, + class_name="flex flex-col gap-10 rounded-xl bakdrop-blur-[16px] [box-shadow:0_-2px_2px_1px_rgba(0,_0,_0,_0.02),_0_1px_1px_0_rgba(0,_0,_0,_0.08),_0_4px_8px_0_rgba(0,_0,_0,_0.03)] bg-white-1 dark:bg-m-slate-11 overflow-hidden group h-full", + ), + display=rx.cond( + (blog_filter_cs.value == "All") | (blog_filter_cs.value == meta["tag"]), + "block", + "none", ), - href=path, - is_external=False, - underline="none", - class_name="box-border flex flex-col flex-shrink-0 border-slate-5 bg-slate-1 shadow-large border border-solid rounded-xl w-full h-[390px] overflow-hidden group", + class_name="relative border-y border-m-slate-4 dark:border-m-slate-10 lg:odd:border-r lg:even:border-l lg:even:before:content-[''] lg:even:before:absolute lg:even:before:w-12 lg:even:before:-left-12 lg:even:before:top-0 lg:even:before:bottom-0 lg:even:before:border-y lg:even:before:border-m-slate-4 lg:dark:even:before:border-m-slate-10", ) -def first_post() -> rx.Component: - for path, document in blog_data.items(): - if path == next(iter(blog_data.keys())): - return first_post_card(meta=document.metadata, path=f"/blog/{path}") - - def component_grid() -> rx.Component: posts = [] for path, document in list(blog_data.items()): posts.append(card_content(meta=document.metadata, path=f"/blog/{path}")) - return rx.box( + return rx.el.div( *posts, - class_name="gap-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 [&>*]:min-w-[320px] w-full mb-4 lg:[&>*:first-child]:hidden", + rx.el.div( + class_name="absolute -top-px -right-24 w-24 h-px bg-gradient-to-l from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden" + ), + rx.el.div( + class_name="absolute -top-px -left-24 w-24 h-px bg-gradient-to-r from-transparent to-current text-m-slate-4 dark:text-m-slate-10 max-lg:hidden" + ), + 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 max-lg:hidden" + ), + 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 max-lg:hidden" + ), + 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" + ), + 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" + ), + class_name="grid lg:grid-cols-2 grid-cols-1 lg:border border-m-slate-4 dark:border-m-slate-10 w-full gap-x-12 gap-y-12 lg:gap-y-24 relative py-24", ) -@webpage(path="/blog", title="Reflex Blog") +@marketing_page( + path="/blog", + title="Reflex Blog", + description="Reflex Blog", + image="/previews/index_preview.webp", + meta=create_meta_tags( + title="Reflex Blog", + description="Reflex Blog", + image="/previews/index_preview.webp", + url="https://reflex.dev/blog", + ), +) def blogs(): return rx.el.section( rx.el.header( - h1_title(title="Blog"), + rx.el.h1( + "Blog", + class_name="text-4xl font-[575] text-m-slate-12 dark:text-m-slate-3 text-center", + ), rx.el.h2( - "Reflex is the open-source framework empowering Python developers to build web apps faster.", - class_name="font-md text-balance text-slate-10", + "Reflex is the open-source framework empowering Python ", + rx.el.br(class_name=""), + " developers to build web apps faster.", + class_name="font-medium text-center text-m-slate-7 dark:text-m-slate-6 text-base", ), - class_name="pb-4 section-header", + class_name="pb-12 pt-24 lg:border-x border-m-slate-4 dark:border-m-slate-10 flex flex-col justify-center items-center gap-6 w-full", ), - first_post(), + blog_filters_row(), component_grid(), id="blog", - class_name="section-content", + class_name="flex flex-col justify-center items-center max-w-(--docs-layout-max-width) mx-auto w-full max-lg:px-4", ) diff --git a/pcweb/pages/blog/page.py b/pcweb/pages/blog/page.py index 404ad909ad..8db8e2d709 100644 --- a/pcweb/pages/blog/page.py +++ b/pcweb/pages/blog/page.py @@ -192,6 +192,11 @@ def page(document, route) -> rx.Component: class_name="text-m-slate-12 dark:text-m-slate-3 font-[575] hover:text-primary-10 dark:hover:text-primary-9 text-xs", ), rx.el.div(class_name="w-4 h-px bg-m-slate-5 dark:bg-m-slate-10"), + rx.el.span( + meta["tag"], + class_name="text-m-slate-12 dark:text-m-slate-3 font-[575] text-xs", + ), + rx.el.div(class_name="w-4 h-px bg-m-slate-5 dark:bg-m-slate-10"), rx.moment( str(meta["date"]), format="MMM DD, YYYY", @@ -259,10 +264,5 @@ def page(document, route) -> rx.Component: ), class_name=ui.cn( "flex flex-col mx-auto max-lg:px-6 w-full relative", - rx.cond( - HostingBannerState.is_banner_visible, - "lg:pt-[7rem] pt-[11rem]", - "lg:pt-[4rem] pt-[8rem]", - ), ), ) diff --git a/pcweb/templates/marketing_page.py b/pcweb/templates/marketing_page.py index 3df41a6519..f03970508f 100644 --- a/pcweb/templates/marketing_page.py +++ b/pcweb/templates/marketing_page.py @@ -2,7 +2,9 @@ from typing import Callable import reflex as rx +import reflex_ui as ui +from pcweb.components.hosting_banner import HostingBannerState from pcweb.route import Route DEFAULT_TITLE = "The platform to build and scale enterprise apps" @@ -72,7 +74,14 @@ def wrapper(*children, **props) -> rx.Component: footer(), 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 relative h-full justify-center items-center", + rx.cond( + HostingBannerState.is_banner_visible, + "mt-28", + "mt-16", + ), + ), ), class_name="flex flex-col w-full justify-center items-center relative dark:bg-m-slate-12 bg-m-slate-1", ) From 5a9c72526147709ce1d3c8160799b9176e14247e Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 27 Feb 2026 15:29:09 +0100 Subject: [PATCH 2/3] show 2 --- pcweb/pages/blog/page.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pcweb/pages/blog/page.py b/pcweb/pages/blog/page.py index 8db8e2d709..9d6b4e3ec2 100644 --- a/pcweb/pages/blog/page.py +++ b/pcweb/pages/blog/page.py @@ -146,19 +146,19 @@ def more_posts(current_post: dict) -> rx.Component: ) if current_index is None: - # If current post is not found, default to first 3 posts - selected_posts = blog_items[:3] + # If current post is not found, default to first 2 posts + selected_posts = blog_items[:2] elif current_index == 0: - # If it's the first post, get the next 3 - selected_posts = blog_items[1:4] + # If it's the first post, get the next 2 + selected_posts = blog_items[1:3] elif current_index == len(blog_items) - 1: - # If it's the last post, get the previous 3 - selected_posts = blog_items[-4:-1] + # If it's the last post, get the previous 2 + selected_posts = blog_items[-3:-1] else: - # Get previous 1 and next 2, excluding current post + # Get previous 1 and next 1, excluding current post selected_posts = ( blog_items[max(0, current_index - 1) : current_index] - + blog_items[current_index + 1 : current_index + 3] + + blog_items[current_index + 1 : current_index + 2] ) for path, document in selected_posts: @@ -171,7 +171,7 @@ def more_posts(current_post: dict) -> rx.Component: ), rx.box( *posts, - class_name="gap-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 [&>*]:min-w-[320px] w-full mb-4 blog-grid", + class_name="gap-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 [&>*]:min-w-[320px] w-full mb-4 blog-grid", ), class_name="flex flex-col gap-10 mt-20", ) From 5e9bf6846f15a5e4a0a1b6ac0d7bdc5e799d28ae Mon Sep 17 00:00:00 2001 From: carlosabadia Date: Fri, 27 Feb 2026 15:32:57 +0100 Subject: [PATCH 3/3] updates --- pcweb/pages/blog/blog.py | 11 ++++++----- pcweb/pages/customers/views/footer.py | 4 ++-- pcweb/pages/framework/views/footer_index.py | 4 ++-- pcweb/pages/framework/views/os_newsletter.py | 4 ++-- pcweb/views/bottom_section/newsletter.py | 4 ++-- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/pcweb/pages/blog/blog.py b/pcweb/pages/blog/blog.py index 3b56647975..81d973349b 100644 --- a/pcweb/pages/blog/blog.py +++ b/pcweb/pages/blog/blog.py @@ -34,9 +34,9 @@ def blog_filters_row() -> rx.Component: return rx.el.div( rx.el.div( *[filter_button(filter) for filter in FILTERS], - class_name="flex flex-row gap-2 items center px-4 h-full", + class_name="flex flex-row gap-2 items-center px-4 h-full", ), - newletter_input(), + newsletter_input(), class_name=ui.cn( "flex flex-row gap-4 items-center justify-between h-[3.25rem] border-x border-m-slate-4 dark:border-m-slate-10 bg-m-slate-1 dark:bg-m-slate-11 [box-shadow:0_-1px_0_0_rgba(0,_0,_0,_0.05),_0_-2px_2px_1px_rgba(0,_0,_0,_0.02),_0_1px_1px_0_rgba(0,_0,_0,_0.08),_0_4px_8px_0_rgba(0,_0,_0,_0.03),_0_1px_0_0_#FFF_inset] w-full z-10 lg:sticky dark:shadow-none max-lg:overflow-x-auto max-lg:-mx-4 max-lg:w-[calc(100%+2rem)]", rx.cond( @@ -48,7 +48,7 @@ def blog_filters_row() -> rx.Component: ) -def newletter_input() -> rx.Component: +def newsletter_input() -> rx.Component: return rx.el.div( rx.cond( IndexState.signed_up, @@ -125,10 +125,11 @@ def card_content(meta: dict, path: str) -> rx.Component: class_name="flex flex-col w-full h-full pb-12 px-12", ), to=path, - class_name="flex flex-col gap-10 rounded-xl bakdrop-blur-[16px] [box-shadow:0_-2px_2px_1px_rgba(0,_0,_0,_0.02),_0_1px_1px_0_rgba(0,_0,_0,_0.08),_0_4px_8px_0_rgba(0,_0,_0,_0.03)] bg-white-1 dark:bg-m-slate-11 overflow-hidden group h-full", + class_name="flex flex-col gap-10 rounded-xl backdrop-blur-[16px] [box-shadow:0_-2px_2px_1px_rgba(0,_0,_0,_0.02),_0_1px_1px_0_rgba(0,_0,_0,_0.08),_0_4px_8px_0_rgba(0,_0,_0,_0.03)] bg-white-1 dark:bg-m-slate-11 overflow-hidden group h-full", ), display=rx.cond( - (blog_filter_cs.value == "All") | (blog_filter_cs.value == meta["tag"]), + (blog_filter_cs.value == "All") + | (blog_filter_cs.value == meta.get("tag", "")), "block", "none", ), diff --git a/pcweb/pages/customers/views/footer.py b/pcweb/pages/customers/views/footer.py index c8c35bf9de..5ce5e5150d 100644 --- a/pcweb/pages/customers/views/footer.py +++ b/pcweb/pages/customers/views/footer.py @@ -65,7 +65,7 @@ def menu_socials() -> rx.Component: ) -def newletter_input() -> rx.Component: +def newsletter_input() -> rx.Component: return rx.box( rx.cond( IndexState.signed_up, @@ -116,7 +116,7 @@ def news_letter() -> rx.Component: "Get updates", class_name="font-small text-slate-9", ), - newletter_input(), + newsletter_input(), class_name="flex flex-col items-start gap-4 self-stretch p-10", ), ) diff --git a/pcweb/pages/framework/views/footer_index.py b/pcweb/pages/framework/views/footer_index.py index ddc411d557..41cd011c5c 100644 --- a/pcweb/pages/framework/views/footer_index.py +++ b/pcweb/pages/framework/views/footer_index.py @@ -92,7 +92,7 @@ def menu_socials() -> rx.Component: ) -def newletter_input() -> rx.Component: +def newsletter_input() -> rx.Component: return rx.box( rx.cond( IndexState.signed_up, @@ -144,7 +144,7 @@ def newsletter() -> rx.Component: "Get updates", class_name="font-small text-secondary-11", ), - newletter_input(), + newsletter_input(), class_name="flex flex-col items-center lg:items-start gap-4 self-stretch p-10 lg:border-r border-slate-3", ), ) diff --git a/pcweb/pages/framework/views/os_newsletter.py b/pcweb/pages/framework/views/os_newsletter.py index 25796d7a96..d8205fff12 100644 --- a/pcweb/pages/framework/views/os_newsletter.py +++ b/pcweb/pages/framework/views/os_newsletter.py @@ -34,7 +34,7 @@ def os_card() -> rx.Component: ) -def newletter_input() -> rx.Component: +def newsletter_input() -> rx.Component: return rx.box( rx.cond( IndexState.signed_up, @@ -123,7 +123,7 @@ def newsletter_card() -> rx.Component: ), class_name="flex flex-col gap-2", ), - newletter_input(), + newsletter_input(), id="newsletter", class_name="flex flex-col gap-8 w-full p-10 pb-12 lg:!border-r !border-slate-3 items-center lg:items-start text-center lg:text-start scroll-mt-72", ) diff --git a/pcweb/views/bottom_section/newsletter.py b/pcweb/views/bottom_section/newsletter.py index e595fcfe20..54d15c2890 100644 --- a/pcweb/views/bottom_section/newsletter.py +++ b/pcweb/views/bottom_section/newsletter.py @@ -4,7 +4,7 @@ from pcweb.signup import IndexState -def newletter_input() -> rx.Component: +def newsletter_input() -> rx.Component: return rx.box( rx.cond( IndexState.signed_up, @@ -91,7 +91,7 @@ def newsletter_card() -> rx.Component: ), class_name="flex flex-col items-center text-center gap-2", ), - newletter_input(), + newsletter_input(), class_name="flex flex-col gap-4 w-full items-center", )